Return to naming_service.cpp CVS log | Up to Nevrax / code / nelns / naming_service |
File: Nevrax / code / nelns / naming_service / naming_service.cpp (download) Revision 1.21, Tue Mar 26 09:45:11 2002 UTC (2 months, 4 weeks ago) by lecroart Branch: MAIN Changes since 1.20: +3 -2 lines CHANGED: unified the command format |
/** \file naming_service.cpp * Naming Service (NS) * * $Id: naming_service.cpp,v 1.21 2002/03/26 09:45:11 lecroart Exp $ */ /* Copyright, 2000 Nevrax Ltd. * * This file is part of NEVRAX NeL Network Services. * NEVRAX NeL Network Services 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 Network Services 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 Network Services; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ #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/displayer.h" #include "nel/net/service.h" // // Namespaces // using namespace std; using namespace NLMISC; using namespace NLNET; // // Structures // struct CServiceEntry { CServiceEntry (TSockId sock, CInetAddress a, string n, TServiceId s) : SockId(sock), Addr(a), Name(n), SId (s) { } TSockId SockId; // the connection between the service and the naming service CInetAddress Addr; // address to send to the service who wants to lookup this service string Name; // name of the service TServiceId SId; // id of the service }; // // 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) // // Functions // void displayRegisteredServices (CCallbackNetBase &netbase) { nlinfo ("Display the %d registered services :", RegisteredServices.size()); for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { nlinfo ("> %s '%s' %s-%hu '%s'", (*it).SockId->asString().c_str(), netbase.hostAddress((*it).SockId).asString().c_str(), (*it).Name.c_str(), (uint16)(*it).SId, (*it).Addr.asString().c_str()); } nlinfo ("End ot the list"); } /* * 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(), (uint16)(*it).SId, (*it).Addr.asString().c_str()); // tell to everybody that this service is unregistered CMessage msgout ("UNB"); msgout.serial ((*it).Name); msgout.serial ((*it).SId); msgout.serial ((*it).Addr); CNetManager::send ("NS", msgout, 0); nlinfo ("Broadcast the Unregistration to everybody"); // remove the service from the registered service list return RegisteredServices.erase (it); } 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", (uint16)sid); } 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 cbLookupSId and cbLookupAlternateSId. *********** OUT OF DATE *************** * Returns NULL if service not found */ void doLookup (const string &sname, TServiceId sid, TSockId from, CCallbackNetBase &netbase, bool sendAll, bool useSId) { nlstop; string name; if (useSId) name = toString (sid); else name = sname; nlinfo ("Lookup for service '%s' for '%s'...", name.c_str(), netbase.hostAddress(from).asString().c_str()); displayRegisteredServices (netbase); // Send list vector<CInetAddress> addrs; list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((useSId && (*it).SId == sid) || (!useSId && (*it).Name == name)) { addrs.push_back ((*it).Addr); // if it only want one, stop now to search if (!sendAll) break; } } if (addrs.size()==0) nldebug ("Service %s is not found", name.c_str()); else if (addrs.size()==1) nldebug ("Service %s is at %s", name.c_str(), addrs[0].asString().c_str()); else nldebug ("%d Services %s found", addrs.size(), name.c_str()); CMessage msgout ("LK"); msgout.serialCont (addrs); netbase.send (msgout, from); } /** * Callback for service look-up. *********** OUT OF DATE *************** * * Message expected : LK * - Name of service to find (string) * * Message emitted : LK * - Validity time in seconds, or 0 if service not found (uint16) * - Address of service if service found, otherwise nothing (CInetAddress) * * \todo Olivier: Select the best service provider, not the first one in the list */ static void cbLookup (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { nlstop; string name; msgin.serial (name); // Find and return a service doLookup (name, 0, from, netbase, false, false); } /** * Callback for alternate service look-up when a service is not responding *********** OUT OF DATE *************** * * Message expected : LA * - Name of service (string) * - Address of server not responding (CInetAddress) * * Message emitted : no name (NS) * - Validity time in seconds, or 0 if service not found (uint16) * - Address of service if service found, otherwise nothing (CInetAddress) */ static void cbLookupAlternate (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { nlstop; string name; CInetAddress addr; msgin.serial (name); msgin.serial (addr); nlinfo ("Server %s looks down, looking-up for alternative service %s", addr.asString().c_str(), name.c_str() ); // Unregister down server doUnregisterService (addr); // Find and return another servive doLookup (name, 0, from, netbase, false, false); } /** * Callback for service look-up for all corresponding to a name. *********** OUT OF DATE *************** * * Message expected : LKA * - Name of service to find (string) * * Message emitted : no name (ALKA) * - List of addresses (vector<CInetAddress>) */ static void cbLookupAll (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { nlstop; // Receive name string name; msgin.serial (name); // Find and return a service doLookup (name, 0, from, netbase, true, false); } /** * Callback for service look-up by identifier *********** OUT OF DATE *************** * * Message expected : LKI * - Identifier of service to find (TServiceId) * * Message emitted : no name (NS) * - Validity time in seconds, or 0 if service not found (uint16) * - Address of service if service found, otherwise nothing (CInetAddress) * * \todo Olivier: Select the best service provider, not the first one in the list */ static void cbLookupSId (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { nlstop; // Receive id TServiceId sid; msgin.serial (sid); // Find and return a service doLookup ("", sid, from, netbase, false, true); } /* * 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 CInetAddress &addr, TServiceId sid, TSockId from, CCallbackNetBase &netbase) { // find if the service is not already registered 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 == 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++; if (sid == 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); ok = false; break; } } if (it != RegisteredServices.end ()) { ok = true; } } // if ok, register the service and send a broadcast to other people if (ok) { RegisteredServices.push_back (CServiceEntry(from, addr, name, sid)); // tell to everybody that this service is registered CMessage msgout ("RGB"); uint8 s = 1; msgout.serial (s); msgout.serial (const_cast<string &>(name)); msgout.serial (sid); msgout.serial (const_cast<CInetAddress &>(addr)); CNetManager::send ("NS", msgout, 0); nlinfo ("Broadcast the Registration to everybody"); } } // send the answer to the client CMessage msgout ("RG"); msgout.serial (ok); if (ok) msgout.serial (sid); netbase.send (msgout, from); netbase.flush (from); displayRegisteredServices (netbase); return ok!=0; } /** * 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; CInetAddress addr; TServiceId sid; msgin.serial (name); msgin.serial (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 (netbase); } /* * 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.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 ("A 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) { doUnregisterService (from); displayRegisteredServices (*CNetManager::getNetBase(serviceName)); } /* * a service is connected, send him all services infos */ static void cbConnect (const string &serviceName, TSockId from, void *arg) { CMessage msgout ("RGB"); uint8 s = RegisteredServices.size (); msgout.serial (s); for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { msgout.serial ((*it).Name); msgout.serial ((*it).SId); msgout.serial ((*it).Addr); } CNetManager::send ("NS", msgout, from); nlinfo ("Sending all services available to the new client"); displayRegisteredServices (*CNetManager::getNetBase(serviceName)); } // // Callback array // TCallbackItem CallbackArray[] = { { "RG", cbRegister }, { "QP", cbQueryPort }, { "UNI", cbUnregisterSId }, }; // // Service // class CNamingService : public NLNET::IService { public: void init() { // if a baseport is available in the config file, get it try { uint16 newBasePort = ConfigFile.getVar ("BasePort").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); } catch (EUnknownVar &) { } // 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() ); } }; // /// Naming Service // NLNET_OLD_SERVICE_MAIN (CNamingService, "NS", "naming_service", 50000, CallbackArray, NELNS_CONFIG, NELNS_LOGS) // // Commands // NLMISC_COMMAND (nsServices, "displays the list of all registered services", "") { if(args.size() != 0) return false; log.displayNL ("Display the %d registered services :", RegisteredServices.size()); for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { log.displayNL ("> %s '%s' %s-%hu '%s'", (*it).SockId->asString().c_str(), CNetManager::getNetBase ("NS")->hostAddress((*it).SockId).asString().c_str(), (*it).Name.c_str(), (uint16)(*it).SId, (*it).Addr.asString().c_str()); } log.displayNL ("End ot the list"); return true; }