diff --git a/MAC/GCF/TM/src/ServiceBroker/GSB_Controller.cc b/MAC/GCF/TM/src/ServiceBroker/GSB_Controller.cc index 360171ca462c42e9939ca8a95213ebb52ccb7ff0..ba4c4c0324a1696ff8c441bb2ffec7b471742540 100644 --- a/MAC/GCF/TM/src/ServiceBroker/GSB_Controller.cc +++ b/MAC/GCF/TM/src/ServiceBroker/GSB_Controller.cc @@ -22,6 +22,7 @@ #include <lofar_config.h> +#include <Common/lofar_fstream.h> #include <APS/ParameterSet.h> #include <GCF/Utils.h> #include <GCF/GCF_ServiceInfo.h> @@ -30,9 +31,9 @@ #include "GSB_Controller.h" namespace LOFAR { - namespace GCF { - using namespace TM; - namespace SB { + namespace GCF { + using namespace TM; + namespace SB { static string sSBTaskName("ServiceBrokerTask"); @@ -40,7 +41,14 @@ static string sSBTaskName("ServiceBrokerTask"); // xxx // GSBController::GSBController() : - GCFTask((State)&GSBController::initial, sSBTaskName) + GCFTask((State)&GSBController::initial, sSBTaskName), + itsServiceList(), + itsListener(), + itsAdminFile(), + itsLowerLimit(0), + itsUpperLimit(0), + itsNrPorts(0), + itsNrFreePorts(0) { // register the protocol for debugging purposes registerProtocol(SB_PROTOCOL, SB_PROTOCOL_signalnames); @@ -51,6 +59,9 @@ GSBController::GSBController() : // read the port range I may use readRanges(); + + // load previous registration if any + loadAdministration (itsAdminFile); } // @@ -103,7 +114,15 @@ GCFEvent::TResult GSBController::operational(GCFEvent& event, GCFPortInterface& GCFEvent::TResult status = GCFEvent::HANDLED; switch (event.signal) { - case F_CONNECTED: + case F_ENTRY: + itsListener.setTimer(300.0); // to cleanup not-reclaimed portnumbers + break; + + case F_TIMER: + cleanupOldRegistrations(); + break; + + case F_CONNECTED: break; case F_ACCEPT_REQ: @@ -127,7 +146,7 @@ GCFEvent::TResult GSBController::operational(GCFEvent& event, GCFPortInterface& response.seqnr = request.seqnr; // Is Service already registered? - if (findService(request.servicename)) { + if (findService(request.servicename, true)) { LOG_ERROR(formatString("Service %s already exist", request.servicename.c_str())); response.result = SB_SERVICE_ALREADY_EXIST; @@ -159,7 +178,7 @@ GCFEvent::TResult GSBController::operational(GCFEvent& event, GCFPortInterface& SBServiceInfoEvent response; response.seqnr = request.seqnr; - uint16 portNr = findService(request.servicename); + uint16 portNr = findService(request.servicename, true); if (portNr) { LOG_INFO(formatString ("Serviceinfo for %s is %d", request.servicename.c_str(), portNr)); @@ -228,6 +247,15 @@ void GSBController::readRanges() LOG_INFO (formatString("Managing portnumbers %d till %d (%d)", itsLowerLimit, itsUpperLimit, itsNrPorts)); + + // Finally read or construct name of adminfile. + if (!ACC::APS::globalParameterSet()->isDefined("adminFile")) { + itsAdminFile = "./SB.admin"; + } + else { + itsAdminFile = ACC::APS::globalParameterSet()->getString("adminFile"); + } + LOG_INFO_STR ("Using file " << itsAdminFile << " for administration"); } // @@ -242,21 +270,32 @@ uint16 GSBController::claimPortNumber(const string& aServiceName, return (0); } - while (idx < itsNrPorts) { // scan whole array - if (itsServiceList[idx].portNumber == 0) { - itsServiceList[idx].portNumber = itsLowerLimit + idx; - itsServiceList[idx].serviceName = aServiceName; - itsServiceList[idx].ownerPort = aPort; - itsNrFreePorts--; - LOG_INFO(formatString ("Portnumber %d assigned to '%s'.", - itsServiceList[idx].portNumber, aServiceName.c_str())); - LOG_INFO_STR ("Managing " << itsNrPorts - itsNrFreePorts << " ports now"); - return (itsServiceList[idx].portNumber); + // note: an loaded registration may already be present, reuse that address + uint16 portNr = findService(aServiceName, false); + if (portNr) { + idx = portNr2Index(portNr); + } + else { + // no old loaded entry available, search free place + while (idx < itsNrPorts && itsServiceList[idx].portNumber != 0) { + idx++; } - idx++; + itsNrFreePorts--; + ASSERTSTR (idx < itsNrPorts, "Major programming error in 'claimPortNumber'!"); } - ASSERTSTR (false, "Major programming error in 'claimPortNumber'!"); + // assign port to service + itsServiceList[idx].portNumber = index2PortNr(idx); + itsServiceList[idx].serviceName = aServiceName; + itsServiceList[idx].ownerPort = aPort; + + // notify user. + LOG_INFO(formatString ("Portnumber %d assigned to '%s'.", + itsServiceList[idx].portNumber, aServiceName.c_str())); + LOG_INFO_STR ("Managing " << itsNrPorts - itsNrFreePorts << " ports now"); + saveAdministration (itsAdminFile); // to survive crashes + + return (itsServiceList[idx].portNumber); } // @@ -268,12 +307,12 @@ void GSBController::releaseService(const string& aServiceName) return; } - uint16 portNr = findService(aServiceName); + uint16 portNr = findService(aServiceName, false); if (!portNr) { // unknown service? return; } - int32 idx = portNr - itsLowerLimit; // convert portnr to array index + int32 idx = portNr2Index(portNr); // convert portnr to array index LOG_INFO(formatString("Service %s(%d) unregistered", aServiceName.c_str(), portNr)); @@ -283,6 +322,8 @@ void GSBController::releaseService(const string& aServiceName) itsNrFreePorts++; LOG_INFO_STR ("Still managing " << itsNrPorts - itsNrFreePorts << " ports"); + + saveAdministration (itsAdminFile); // to survive crashes } // @@ -296,6 +337,7 @@ void GSBController::releasePort(GCFPortInterface* aPort) int32 idx = 0; int32 nrElems2Check = itsNrPorts - itsNrFreePorts;// prevent checking whole array + int32 orgCount = nrElems2Check; while (idx < itsNrPorts && nrElems2Check > 0) { if (itsServiceList[idx].portNumber) { // used entry? @@ -313,12 +355,16 @@ void GSBController::releasePort(GCFPortInterface* aPort) idx++; } LOG_INFO_STR ("Still managing " << itsNrPorts - itsNrFreePorts << " ports"); + + if (orgCount != (itsNrPorts - itsNrFreePorts)) { + saveAdministration (itsAdminFile); + } } // // findService(serviceName) // -uint16 GSBController::findService(const string& aServiceName) +uint16 GSBController::findService(const string& aServiceName, bool usedOnly) { int32 idx = 0; int32 nrElems2Check = itsNrPorts - itsNrFreePorts;// prevent checking whole array @@ -327,7 +373,10 @@ uint16 GSBController::findService(const string& aServiceName) if (itsServiceList[idx].portNumber) { nrElems2Check--; if (itsServiceList[idx].serviceName == aServiceName) { - return (itsServiceList[idx].portNumber); + // only return portnumber if port is assigned also to prevent + // returning numbers of old, not-reregistered, services + return ((usedOnly && !itsServiceList[idx].ownerPort) ? 0 : + itsServiceList[idx].portNumber); } } idx++; @@ -336,6 +385,112 @@ uint16 GSBController::findService(const string& aServiceName) return (0); } +// +// saveAdministration(fileName) +// +void GSBController::saveAdministration(const string& aFileName) +{ + // Try to create the administration file. + ofstream outFile(aFileName.c_str(), ofstream::out | ofstream::trunc + | ofstream::binary); + + // If the file can not be opened warn to operator. + if (!outFile) { + LOG_WARN_STR("Unable to open file " << aFileName + << ". ServiceBroker is not powerfailure save!"); + return; + } + + LOG_DEBUG ("Saving administration"); + + uint16 writeVersion = SB_ADMIN_VERSION; + uint16 count = itsNrPorts - itsNrFreePorts; + outFile << writeVersion << count << itsLowerLimit << itsUpperLimit; + + uint16 idx = 0; + while (idx < itsNrPorts && count > 0) { + if (itsServiceList[idx].portNumber) { + // note: the TCPport is not saved because it can not be restored. + outFile << itsServiceList[idx].portNumber << itsServiceList[idx].serviceName; + count--; + } + idx++; + } + outFile.close(); +} + +// +// loadAdministration(filename) +// +void GSBController::loadAdministration(const string& aFileName) +{ + // Try to open the adminfile. + ifstream inFile(aFileName.c_str(), ifstream::in | ifstream::binary); + if (!inFile) { + LOG_DEBUG_STR ("No old administration found(" << aFileName << ")"); + return; + } + + // read header info + uint16 readVersion; // for future version management + uint16 count; // nr of entries in the file + uint16 lowerLimit; // range settings + uint16 upperLimit; + inFile >> readVersion >> count >> lowerLimit >> upperLimit; + + LOG_DEBUG_STR ("Loading " << count << " old registrations from file " << aFileName); + + while (count) { + uint16 portNumber; + string serviceName; + inFile >> portNumber >> serviceName; + if (portNumber < itsLowerLimit || portNumber >= itsUpperLimit) { + LOG_DEBUG_STR ("Portnumber " << portNumber + << " not in current range, ignoring"); + } + else { + itsServiceList[portNr2Index(portNumber)].portNumber = portNumber; + itsServiceList[portNr2Index(portNumber)].serviceName = serviceName; + itsServiceList[portNr2Index(portNumber)].ownerPort = 0; + itsNrFreePorts--; + LOG_DEBUG_STR ("Loading " << serviceName << "@" << portNumber); + } + count--; + } + + inFile.close(); +} + +// +// cleanupOldRegistrations() +// +void GSBController::cleanupOldRegistrations() +{ + LOG_DEBUG ("Cleanup of old not reclaimed registrations"); + + bool removedSome = false; + uint16 idx = 0; + while (idx < itsNrPorts) { + if (itsServiceList[idx].portNumber && !itsServiceList[idx].ownerPort) { + LOG_DEBUG_STR ("Cleanup old " << itsServiceList[idx].serviceName + << "@" << itsServiceList[idx].portNumber); + itsServiceList[idx].portNumber = 0; + itsServiceList[idx].serviceName = ""; + itsServiceList[idx].ownerPort = 0; + itsNrFreePorts++; + removedSome = true; + } + idx++; + } + + LOG_INFO_STR ("Now managing " << itsNrPorts - itsNrFreePorts << " ports"); + + if (removedSome) { + saveAdministration(itsAdminFile); + } +} + + } // namespace SB } // namespace GCF } // namespace LOFAR diff --git a/MAC/GCF/TM/src/ServiceBroker/GSB_Controller.h b/MAC/GCF/TM/src/ServiceBroker/GSB_Controller.h index dbfaaf72ae5438502bbdc59f9ba731c31af5d21c..019a27728a222a68d56d890c64a90eacdbae6dae 100644 --- a/MAC/GCF/TM/src/ServiceBroker/GSB_Controller.h +++ b/MAC/GCF/TM/src/ServiceBroker/GSB_Controller.h @@ -52,6 +52,8 @@ private: TM::GCFEvent::TResult initial (TM::GCFEvent& e, TM::GCFPortInterface& p); TM::GCFEvent::TResult operational(TM::GCFEvent& e, TM::GCFPortInterface& p); + #define SB_ADMIN_VERSION 0x0100 + typedef struct { uint16 portNumber; string serviceName; @@ -63,12 +65,23 @@ private: uint16 claimPortNumber (const string& aServiceName, TM::GCFPortInterface* aPort); void releaseService (const string& aServiceName); void releasePort (TM::GCFPortInterface* aPort); - uint16 findService (const string& aServiceName); + uint16 findService (const string& aServiceName, bool usedOnly); + void saveAdministration (const string& aFileName); + void loadAdministration (const string& aFileName); + void cleanupOldRegistrations(); + + // define conversions between portnumber and index. + inline uint16 portNr2Index(uint16 portNumber) + { return (portNumber - itsLowerLimit); } + inline uint16 index2PortNr(uint16 index) + { return (index + itsLowerLimit); } //# --- data members --- vector<TServiceInfo> itsServiceList; // the administration GTMSBTCPPort itsListener; // for all SB protocol messages + string itsAdminFile; // to survive crashes + uint16 itsLowerLimit; // lowest portnr to assign uint16 itsUpperLimit; // assign till this number uint16 itsNrPorts; // number of ports to manage diff --git a/MAC/GCF/TM/src/ServiceBroker/ServiceBroker.conf.in b/MAC/GCF/TM/src/ServiceBroker/ServiceBroker.conf.in index c59a829c2cccf0e3a437da557333ba39f52df48b..6ff2635e84637472c0cc4298d3c1498af6bf6580 100644 --- a/MAC/GCF/TM/src/ServiceBroker/ServiceBroker.conf.in +++ b/MAC/GCF/TM/src/ServiceBroker/ServiceBroker.conf.in @@ -4,3 +4,6 @@ firstPortNumber = 24001 lastPortNumber = 25000 +# optional parameters +adminFile = ./SB.admin # is also the default name +