From 7a2e25f79cbefdb7808fb9963e103688e548e18b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Dec 2015 22:47:35 -0500 Subject: [PATCH 01/51] rewrite Properties let's be rid of some egregiously bad C++ code... --- src/utils/configuration.cpp | 268 +++++++++------------------- src/utils/configuration.h | 39 ++-- testApp/utils/configurationTest.cpp | 65 ++++++- 3 files changed, 160 insertions(+), 212 deletions(-) diff --git a/src/utils/configuration.cpp b/src/utils/configuration.cpp index 10dcd6d..28deee8 100644 --- a/src/utils/configuration.cpp +++ b/src/utils/configuration.cpp @@ -24,211 +24,109 @@ namespace pvAccess { using namespace epics::pvData; using namespace std; -Properties::Properties() -{ - _fileName = ""; - _infile.reset(new ifstream()); - _infile->exceptions (ifstream::failbit | ifstream::badbit ); - _outfile.reset(new ofstream()); - _outfile->exceptions (ofstream::failbit | ofstream::badbit ); -} +Properties::Properties() {} -Properties::Properties(const string &fileName) -{ - _fileName = fileName; - _infile.reset(new ifstream()); - _infile->exceptions (ifstream::failbit | ifstream::badbit ); - _outfile.reset(new ofstream()); - _outfile->exceptions (ofstream::failbit | ofstream::badbit ); -} +Properties::Properties(const string &fileName) : _fileName(fileName) {} -Properties::~Properties() +const std::string &Properties::getProperty(const string &key) const { -} - -void Properties::setProperty(const string &key, const string &value) -{ - string oldValue; - std::map::iterator propertiesIterator = _properties.find(key); - - if(propertiesIterator != _properties.end()) //found in map - { - _properties.erase(propertiesIterator); - } - _properties[key] = value; -} - -string Properties::getProperty(const string &key) -{ - std::map::iterator propertiesIterator = _properties.find(key); - if(propertiesIterator != _properties.end()) //found in map - { - return string(propertiesIterator->second); - } - else - { - string errMsg = "Property not found in the map: " + key; - THROW_BASE_EXCEPTION(errMsg.c_str()); + _properties_t::const_iterator propertiesIterator = _properties.find(key); + if(propertiesIterator != _properties.end()) { + return propertiesIterator->second; + } else { + THROW_BASE_EXCEPTION(string("Property not found in the map: ") + key); } } -string Properties::getProperty(const string &key, const string &defaultValue) +const std::string &Properties::getProperty(const string &key, const string &defaultValue) const { - std::map::iterator propertiesIterator = _properties.find(key); - if(propertiesIterator != _properties.end()) //found in map - { - return string(propertiesIterator->second); - } - - return defaultValue; + _properties_t::const_iterator propertiesIterator = _properties.find(key); + if(propertiesIterator != _properties.end()) { + return propertiesIterator->second; + } else { + return defaultValue; + } } -bool Properties::hasProperty(const string &key) +void Properties::Properties::load() { - return (_properties.find(key) != _properties.end()); -} - -void Properties::load() -{ - _properties.clear(); - -#ifdef NO_STREAM_EXCEPTIONS - _infile->open(_fileName.c_str(),ifstream::in); - if (_infile->fail()) -#else - try - { - _infile->open(_fileName.c_str(),ifstream::in); - } - catch (ifstream::failure& e) -#endif - { - string errMsg = "Error opening file: " + string(_fileName.c_str()); - THROW_BASE_EXCEPTION(errMsg.c_str()); - } - - string line; - string property; - string key; -#ifndef NO_STREAM_EXCEPTIONS - try - { -#endif - while(!_infile->eof()) - { - line.erase(); - std::getline(*_infile,line); - -#ifdef NO_STREAM_EXCEPTIONS - if (_infile->fail()) - { - _infile->close(); - if(_infile->eof()) - { - return; //end of file - } - string errMsg = "Error reading file: " + _fileName; - THROW_BASE_EXCEPTION(errMsg.c_str()); - } -#endif - - //remove trailing spaces - truncate(line); - - //empty line - if(line.length() == 0) - { - continue; - } - // comment - if(line[0] == '#') - { - continue; - } - - //line is in format: propertyName=propertyValue - size_t pos = line.find_first_of('=',0); - if(pos == string::npos) //bad value (= not found) - { - string errMsg = "Bad property line found: " + line; - THROW_BASE_EXCEPTION(errMsg.c_str()); - } - - key = line.substr(0,pos); - truncate(key); - property = line.substr(pos + 1,line.length()); - truncate(property); - _properties[key] = property; - } -#ifndef NO_STREAM_EXCEPTIONS - } - catch (ifstream::failure& e) - { - _infile->close(); - if(_infile->eof()) - { - return; //end of file - } - string errMsg = "Error reading file: " + _fileName; - THROW_BASE_EXCEPTION(errMsg.c_str()); - } -#endif - _infile->close(); + load(_fileName); } void Properties::load(const string &fileName) { - _fileName = fileName; - load(); + ifstream strm(fileName.c_str()); + load(strm); } -void Properties::store() +namespace { +string trim(const string& in) { -#ifdef NO_STREAM_EXCEPTIONS - _outfile->open(_fileName.c_str(),ifstream::trunc); - if (_outfile->fail()) -#else - try - { - _outfile->open(_fileName.c_str(),ifstream::trunc); - } - catch (ofstream::failure& e) -#endif - { - string errMsg = "Error opening file: " + string(_fileName.c_str()); - THROW_BASE_EXCEPTION(errMsg.c_str()); - } - - - for (std::map::iterator propertiesIterator = _properties.begin(); - propertiesIterator != _properties.end(); - propertiesIterator++ ) - { -#ifndef NO_STREAM_EXCEPTIONS - try - { -#endif - string line = string(propertiesIterator->first) + string("=") + string(propertiesIterator->second) + string("\n"); - _outfile->write(line.c_str(),line.length()); -#ifdef NO_STREAM_EXCEPTIONS - if(_outfile->fail()) -#else - } - catch (ofstream::failure& e) -#endif - { - _outfile->close(); - string errMsg = "Error writing to file: " + string(_fileName.c_str()); - THROW_BASE_EXCEPTION(errMsg.c_str()); - } - } - _outfile->close(); + size_t A = in.find_first_not_of(" \t\r"), + B = in.find_last_not_of(" \t\r"); + if(A==B) + return string(); + else + return in.substr(A, B-A+1); +} } -void Properties::store(const string &fileName) +void Properties::load(std::istream& strm) { - _fileName = fileName; - store(); + _properties_t newmap; + + std::string line; + unsigned lineno = 0; + while(getline(strm, line).good()) { + lineno++; + size_t idx = line.find_first_not_of(" \t\r"); + if(idx==line.npos || line[idx]=='#') + continue; + + idx = line.find_first_of('='); + if(idx==line.npos) { + ostringstream msg; + msg<<"Malformed line "<first << " = " << it->second << "\n"; + } } void Properties::list() diff --git a/src/utils/configuration.h b/src/utils/configuration.h index e50cd88..555cbc5 100644 --- a/src/utils/configuration.h +++ b/src/utils/configuration.h @@ -41,36 +41,27 @@ class epicsShareClass Properties public: Properties(); Properties(const std::string &fileName); - virtual ~Properties(); - void setProperty(const std::string &key,const std::string &value); - std::string getProperty(const std::string &key); - std::string getProperty(const std::string &key, const std::string &defaultValue); - bool hasProperty(const std::string &key); + inline void setProperty(const std::string &key,const std::string &value) + { _properties[key] = value; } + const std::string& getProperty(const std::string &key) const; + const std::string& getProperty(const std::string &key, const std::string &defaultValue) const; + inline bool hasProperty(const std::string &key) const + { return _properties.find(key) != _properties.end(); } - void store(); - void store(const std::string &fileName); - void load(); + void store() const; + void store(const std::string &fileName) const; + void store(std::ostream& strm) const; + void load(); void load(const std::string &fileName); + void load(std::istream& strm); void list(); + size_t size() const {return _properties.size();} private: - std::map _properties; - std::auto_ptr _infile; - std::auto_ptr _outfile; - std::string _fileName; - - inline void truncate(std::string& str) - { - while(str.length() != 0 && (str.at(0) == ' ' || str.at(0) == '\t')) - { - str.erase(0,1); - } - while(str.length() != 0 && (str.at(str.length()-1) == ' ' || str.at(str.length()-1) == '\t')) - { - str.erase(str.length()-1,1); - } - } + typedef std::map _properties_t; + _properties_t _properties; + std::string _fileName; }; diff --git a/testApp/utils/configurationTest.cpp b/testApp/utils/configurationTest.cpp index 4ed339f..8348f2b 100644 --- a/testApp/utils/configurationTest.cpp +++ b/testApp/utils/configurationTest.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,59 @@ void setenv(char * a, char * b, int c) using namespace epics::pvAccess; using namespace epics::pvData; -using namespace std; + +static const char indata[] = + "hello = world \n" + " # oops\n" + " #dd=da\n" + " empty = \n" + " this = is a test\n\n" + ; + +static const char expectdata[] = + "empty = \n" + "hello = world\n" + "this = is a test\n" + ; + +static +void showEscaped(const char *msg, const std::string& s) +{ + std::vector chars(epicsStrnEscapedFromRawSize(s.c_str(), s.size())+1); + epicsStrnEscapedFromRaw(&chars[0], chars.size(), s.c_str(), s.size()); + testDiag("%s: '%s", msg, &chars[0]); +} + +static +void testProp() +{ + Properties plist; + + { + std::istringstream input(indata); + plist.load(input); + plist.list(); + testOk1(!input.bad()); + testOk1(input.eof()); + } + + testOk1(plist.size()==3); + testOk1(plist.getProperty("hello")=="world"); + testOk1(plist.getProperty("this")=="is a test"); + testOk1(!plist.hasProperty("foobar")); + + { + std::ostringstream output; + plist.store(output); + std::string expect(expectdata), actual(output.str()); + + testOk1(!output.bad()); + testOk(expect.size()==actual.size(), "%u == %u", (unsigned)expect.size(), (unsigned)actual.size()); + testOk1(actual==expectdata); + showEscaped("actual", actual); + showEscaped("expect", expect); + } +} static void showEnv(const char *name) { @@ -60,9 +113,9 @@ static void showAddr(const osiSockAddr& addr) } while(0) -MAIN(configurationTest) +static +void testConfig() { - testPlan(35); testDiag("Default configuration"); Configuration::shared_pointer configuration(new SystemConfigurationImpl()); @@ -126,7 +179,13 @@ MAIN(configurationTest) Configuration::shared_pointer configurationOut(configProvider->getConfiguration("conf1")); testOk1(configurationOut.get() == configuration.get()); +} +MAIN(configurationTest) +{ + testPlan(44); + testProp(); + testConfig(); return testDone(); } From b3f78718b05a7be59ff79304d2ea86cf3147dd41 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Dec 2015 08:09:10 -0500 Subject: [PATCH 02/51] update Configuration move string parsing into the base class. Add several sub-classes for different configuration sources (environment, std::map), and ConfigurationStack to allow chaining of different sources. Mark Properties as deprecated, just use std::map by way of ConfigurationMap. --- src/utils/configuration.cpp | 139 ++++++++++++++++-------------------- src/utils/configuration.h | 79 +++++++++++++------- 2 files changed, 113 insertions(+), 105 deletions(-) diff --git a/src/utils/configuration.cpp b/src/utils/configuration.cpp index 28deee8..bd1ccf0 100644 --- a/src/utils/configuration.cpp +++ b/src/utils/configuration.cpp @@ -9,6 +9,7 @@ #include #include +#include #define epicsExportSharedSymbols #include @@ -139,105 +140,63 @@ void Properties::list() } } -SystemConfigurationImpl::SystemConfigurationImpl() : - _properties(new Properties()) +bool Configuration::getPropertyAsBoolean(const std::string &name, const bool defaultValue) const { - // no exception, default value is taken - //_ibuffer.exceptions ( ifstream::failbit | ifstream::badbit ); - //_obuffer.exceptions ( ifstream::failbit | ifstream::badbit ); -} + string value = getPropertyAsString(name, defaultValue ? "1" : "0"); + std::transform(value.begin(), value.end(), value.begin(), ::tolower); -SystemConfigurationImpl::~SystemConfigurationImpl() -{ -} - -bool SystemConfigurationImpl::getPropertyAsBoolean(const string &name, const bool defaultValue) -{ - /* - bool retval; - _ibuffer.clear(); - _obuffer.clear(); - _obuffer.str(""); - _obuffer << defaultValue; - _ibuffer.str(getPropertyAsString(name,_obuffer.str())); - _ibuffer >> retval; - if (_ibuffer.fail() || _ibuffer.bad()) - return defaultValue; - else - return retval; - */ - - string value = getPropertyAsString(name, defaultValue ? "1" : "0"); - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - - bool isTrue = (value == "1") || (value == "true") || (value == "yes"); + bool isTrue = (value == "1") || (value == "true") || (value == "yes"); if (isTrue) return true; - bool isFalse = (value == "0") || (value == "false") || (value == "no"); + bool isFalse = (value == "0") || (value == "false") || (value == "no"); if (isFalse) return false; - // invalid value + // invalid value return defaultValue; } -int32 SystemConfigurationImpl::getPropertyAsInteger(const string &name, const int32 defaultValue) +epics::pvData::int32 Configuration::getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue) const { - int32 retval = defaultValue; - _ibuffer.clear(); - _obuffer.clear(); - _obuffer.str(""); - _obuffer << defaultValue; - _ibuffer.str(getPropertyAsString(name, _obuffer.str())); - _ibuffer >> retval; - if (_ibuffer.fail() || _ibuffer.bad()) + epicsInt32 ret; + std::string val(getPropertyAsString(name, "")); + + if(epicsParseInt32(val.c_str(), &ret, 0, NULL)) return defaultValue; - else - return retval; + return ret; } -float SystemConfigurationImpl::getPropertyAsFloat(const string &name, const float defaultValue) +float Configuration::getPropertyAsFloat(const std::string &name, const float defaultValue) const { - float retval; - _ibuffer.clear(); - _obuffer.clear(); - _obuffer.str(""); - _obuffer << defaultValue; - _ibuffer.str(getPropertyAsString(name, _obuffer.str())); - _ibuffer >> retval; - if (_ibuffer.fail() || _ibuffer.bad()) + float ret; + std::string val(getPropertyAsString(name, "")); + + if(epicsParseFloat(val.c_str(), &ret, NULL)) return defaultValue; - else - return retval; + return ret; } -float SystemConfigurationImpl::getPropertyAsDouble(const string &name, const double defaultValue) +double Configuration::getPropertyAsDouble(const std::string &name, const double defaultValue) const { - float retval; - _ibuffer.clear(); - _obuffer.clear(); - _obuffer.str(""); - _obuffer << defaultValue; - _ibuffer.str(getPropertyAsString(name, _obuffer.str())); - _ibuffer >> retval; - if (_ibuffer.fail() || _ibuffer.bad()) + double ret; + std::string val(getPropertyAsString(name, "")); + + if(epicsParseDouble(val.c_str(), &ret, NULL)) return defaultValue; - else - return retval; + return ret; } -string SystemConfigurationImpl::getPropertyAsString(const string &name, const string &defaultValue) +std::string Configuration::getPropertyAsString(const std::string &name, const std::string &defaultValue) const { - const char* val = getenv(name.c_str()); - if(val != NULL) - { - return _properties->getProperty(name, string(val)); - } - return _properties->getProperty(name, defaultValue); + std::string val; + if(tryGetPropertyAsString(name, &val)) + return val; + else + return defaultValue; } -bool SystemConfigurationImpl::getPropertyAsAddress(const std::string& name, osiSockAddr* addr) +bool Configuration::getPropertyAsAddress(const std::string& name, osiSockAddr* addr) const { unsigned short dftport=0; if(addr->sa.sa_family==AF_INET) @@ -253,19 +212,41 @@ bool SystemConfigurationImpl::getPropertyAsAddress(const std::string& name, osiS return true; } -bool SystemConfigurationImpl::hasProperty(const string &key) +bool Configuration::hasProperty(const std::string &name) const { - const char* val = getenv(key.c_str()); - return (val != NULL) || _properties->hasProperty(key); + return tryGetPropertyAsString(name, NULL); } -ConfigurationProviderImpl::ConfigurationProviderImpl() +bool ConfigurationMap::tryGetPropertyAsString(const std::string& name, std::string* val) const { - + properties_t::const_iterator it = properties.find(name); + if(it==properties.end()) + return false; + if(val) + *val = it->second; + return true; } -ConfigurationProviderImpl::~ConfigurationProviderImpl() +bool ConfigurationEnviron::tryGetPropertyAsString(const std::string& name, std::string* val) const { + const char *env = getenv(name.c_str()); + if(!env || !*env) + return false; + if(val) + *val = env; + return true; +} + +bool ConfigurationStack::tryGetPropertyAsString(const std::string& name, std::string* val) const +{ + for(confs_t::const_reverse_iterator it = confs.rbegin(), end = confs.rend(); + it!=end; ++it) + { + Configuration& conf = **it; + if(conf.tryGetPropertyAsString(name, val)) + return true; + } + return false; } void ConfigurationProviderImpl::registerConfiguration(const string &name, Configuration::shared_pointer const & configuration) diff --git a/src/utils/configuration.h b/src/utils/configuration.h index 555cbc5..5b4fb60 100644 --- a/src/utils/configuration.h +++ b/src/utils/configuration.h @@ -39,8 +39,8 @@ namespace pvAccess { class epicsShareClass Properties { public: - Properties(); - Properties(const std::string &fileName); + Properties() EPICS_DEPRECATED; + Properties(const std::string &fileName) EPICS_DEPRECATED; inline void setProperty(const std::string &key,const std::string &value) { _properties[key] = value; } @@ -57,14 +57,16 @@ public: void load(std::istream& strm); void list(); - size_t size() const {return _properties.size();} + inline size_t size() const {return _properties.size();} private: typedef std::map _properties_t; _properties_t _properties; std::string _fileName; +public: + inline const _properties_t& map() const {return _properties;} }; - +class ConfigurationStack; /** * Configuration @@ -87,7 +89,7 @@ public: * * @return environment variable value as bool or default value if it does not exist. */ - virtual bool getPropertyAsBoolean(const std::string &name, const bool defaultValue) = 0; + bool getPropertyAsBoolean(const std::string &name, const bool defaultValue) const; /** * Get the environment variable specified by name or return default value * if it does not exist. @@ -97,7 +99,7 @@ public: * * @return environment variable value as int32 or default value if it does not exist. */ - virtual epics::pvData::int32 getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue) = 0; + epics::pvData::int32 getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue) const; /** * Get the environment variable specified by name or return default value * if it does not exist. @@ -107,7 +109,7 @@ public: * * @return environment variable value as float or default value if it does not exist. */ - virtual float getPropertyAsFloat(const std::string &name, const float defaultValue) = 0; + float getPropertyAsFloat(const std::string &name, const float defaultValue) const; /** * Get the environment variable specified by name or return default value * if it does not exist. @@ -117,7 +119,7 @@ public: * * @return environment variable value as double or default value if it does not exist. */ - virtual float getPropertyAsDouble(const std::string &name, const double defaultValue) = 0; + double getPropertyAsDouble(const std::string &name, const double defaultValue) const; /** * Get the environment variable specified by name or return default value * if it does not exist. @@ -127,7 +129,7 @@ public: * * @return environment variable value as std::string or default value if it does not exist. */ - virtual std::string getPropertyAsString(const std::string &name, const std::string &defaultValue) = 0; + std::string getPropertyAsString(const std::string &name, const std::string &defaultValue) const; /** * Fetch and parse as a socket address and port number (address family set accordingly). * At present only numeric addresses are parsed (eg. "127.0.0.1:4242"). @@ -138,28 +140,53 @@ public: * @pram addr pointer to the address struct to be filled in * @return true if addr now contains an address, false otherwise */ - virtual bool getPropertyAsAddress(const std::string& name, osiSockAddr* addr) = 0; + bool getPropertyAsAddress(const std::string& name, osiSockAddr* addr) const; - virtual bool hasProperty(const std::string &name) = 0; + bool hasProperty(const std::string &name) const; + +protected: + friend class ConfigurationStack; + virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const = 0; }; -class epicsShareClass SystemConfigurationImpl: public Configuration +//! Lookup configuration strings from an in memory store +class epicsShareClass ConfigurationMap: public Configuration { public: - SystemConfigurationImpl(); - ~SystemConfigurationImpl(); - bool getPropertyAsBoolean(const std::string &name, const bool defaultValue); - epics::pvData::int32 getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue); - float getPropertyAsFloat(const std::string &name, const float defaultValue); - float getPropertyAsDouble(const std::string &name, const double defaultValue); - std::string getPropertyAsString(const std::string &name, const std::string &defaultValue); - bool getPropertyAsAddress(const std::string& name, osiSockAddr* addr); - bool hasProperty(const std::string &name); - std::auto_ptr _properties; + typedef std::map properties_t; + properties_t properties; private: - std::istringstream _ibuffer; - std::ostringstream _obuffer; + virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const; +}; +//! Lookup configuration strings from the process environment +class epicsShareClass ConfigurationEnviron: public Configuration +{ +private: + virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const; +}; + +typedef ConfigurationEnviron SystemConfigurationImpl; + +//! Lookup configuration strings from a heap of sub-Configurations. +//! Most recently push'd is checked first. +class epicsShareClass ConfigurationStack : public Configuration +{ + typedef std::vector > confs_t; + confs_t confs; + virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const; +public: + inline void push_back(const confs_t::value_type& conf) { + confs.push_back(conf); + } + inline confs_t::value_type pop_back() { + if(confs.empty()) + throw std::runtime_error("Stack empty"); + confs_t::value_type ret(confs.back()); + confs.pop_back(); + return ret; + } + inline size_t size() const {return confs.size();} }; /** @@ -193,11 +220,11 @@ public: class ConfigurationProviderImpl: public ConfigurationProvider { public: - ConfigurationProviderImpl(); + ConfigurationProviderImpl() {} /** * Destructor. Note: Registered configurations will be deleted!! */ - ~ConfigurationProviderImpl(); + ~ConfigurationProviderImpl() {} Configuration::shared_pointer getConfiguration(const std::string &name); void registerConfiguration(const std::string &name, Configuration::shared_pointer const & configuration); private: From 3eb77754939fef9906d87ca7edb3b3f94944cdd5 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Dec 2015 09:59:15 -0500 Subject: [PATCH 03/51] introduce ConfigurationBuilder --- src/utils/configuration.cpp | 56 ++++++++++++++++++++++++++--- src/utils/configuration.h | 31 ++++++++++++++++ testApp/utils/configurationTest.cpp | 23 ++++++++++-- 3 files changed, 104 insertions(+), 6 deletions(-) diff --git a/src/utils/configuration.cpp b/src/utils/configuration.cpp index bd1ccf0..6b0a356 100644 --- a/src/utils/configuration.cpp +++ b/src/utils/configuration.cpp @@ -249,6 +249,52 @@ bool ConfigurationStack::tryGetPropertyAsString(const std::string& name, std::st return false; } +ConfigurationBuilder::ConfigurationBuilder() :stack(new ConfigurationStack) {} + +ConfigurationBuilder& ConfigurationBuilder::push_env() +{ + Configuration::shared_pointer env(new ConfigurationEnviron); + stack->push_back(env); + return *this; +} + +ConfigurationBuilder& ConfigurationBuilder::push_map() +{ + Configuration::shared_pointer env(new ConfigurationMap(mymap)); + stack->push_back(env); + mymap.clear(); + return *this; +} + +ConfigurationBuilder& +ConfigurationBuilder::push_config(const Configuration::shared_pointer& conf) +{ + stack->push_back(conf); + return *this; +} + +ConfigurationBuilder& +ConfigurationBuilder::_add(const std::string& name, const std::string& val) +{ + if(name.find_first_of(" \t\r\n")!=name.npos) + THROW_EXCEPTION2(std::invalid_argument, "Key name may not contain whitespace"); + mymap[name] = val; + return *this; +} + +Configuration::shared_pointer ConfigurationBuilder::build() +{ + if(!mymap.empty()) + THROW_EXCEPTION2(std::logic_error, "Missing call to .push_map()"); + if(stack->size()==0) { + return Configuration::shared_pointer(new ConfigurationMap); // empty map + } else if(stack->size()==1) { + return stack->pop_back(); + } else { + return stack; + } +} + void ConfigurationProviderImpl::registerConfiguration(const string &name, Configuration::shared_pointer const & configuration) { Lock guard(_mutex); @@ -263,12 +309,15 @@ void ConfigurationProviderImpl::registerConfiguration(const string &name, Config Configuration::shared_pointer ConfigurationProviderImpl::getConfiguration(const string &name) { - std::map::iterator configsIter = _configs.find(name); + Lock guard(_mutex); + std::map::iterator configsIter = _configs.find(name); if(configsIter != _configs.end()) { return configsIter->second; } - return Configuration::shared_pointer(); + Configuration::shared_pointer env(new ConfigurationEnviron); // default to environment only + _configs[name] = env; // ensure that a later attempt to define this config will fail + return env; } ConfigurationProvider::shared_pointer configurationProvider; @@ -280,8 +329,7 @@ ConfigurationProvider::shared_pointer ConfigurationFactory::getProvider() if(configurationProvider.get() == NULL) { configurationProvider.reset(new ConfigurationProviderImpl()); - // default - Configuration::shared_pointer systemConfig(new SystemConfigurationImpl()); + Configuration::shared_pointer systemConfig(new ConfigurationEnviron); configurationProvider->registerConfiguration("system", systemConfig); } return configurationProvider; diff --git a/src/utils/configuration.h b/src/utils/configuration.h index 5b4fb60..81a6ef9 100644 --- a/src/utils/configuration.h +++ b/src/utils/configuration.h @@ -155,6 +155,8 @@ class epicsShareClass ConfigurationMap: public Configuration public: typedef std::map properties_t; properties_t properties; + ConfigurationMap() {} + ConfigurationMap(const properties_t& p) :properties(p) {} private: virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const; }; @@ -189,6 +191,27 @@ public: inline size_t size() const {return confs.size();} }; +struct ConfigurationBuilder +{ + ConfigurationBuilder(); + ConfigurationBuilder& push_env(); + ConfigurationBuilder& push_map(); + ConfigurationBuilder& push_config(const Configuration::shared_pointer&); + template + ConfigurationBuilder& add(const std::string& name, const V& val) + { + std::ostringstream strm; + strm< stack; + friend ConfigurationBuilder& operator<<(ConfigurationBuilder&, const std::string& s); +}; + /** * Configuration provider. */ @@ -249,6 +272,14 @@ public: * @return configuration provider */ static ConfigurationProvider::shared_pointer getProvider(); + static void registerConfiguration(const std::string &name, Configuration::shared_pointer const & configuration) + { + getProvider()->registerConfiguration(name, configuration); + } + static Configuration::shared_pointer getConfiguration(const std::string& name) + { + return getProvider()->getConfiguration(name); + } private: ConfigurationFactory() {}; diff --git a/testApp/utils/configurationTest.cpp b/testApp/utils/configurationTest.cpp index 8348f2b..b10dc4e 100644 --- a/testApp/utils/configurationTest.cpp +++ b/testApp/utils/configurationTest.cpp @@ -62,7 +62,6 @@ void testProp() { std::istringstream input(indata); plist.load(input); - plist.list(); testOk1(!input.bad()); testOk1(input.eof()); } @@ -96,6 +95,25 @@ static void setEnv(const char *name, const char *val) testDiag("%s = \"%s\"", name, getenv(name)); } +static void testBuilder() +{ + Configuration::shared_pointer C(ConfigurationBuilder() + .add("TESTKEY","value1") + .push_map() + .push_env() + .add("OTHERKEY","value3") + .push_map() + .build()); + + testOk1(C->getPropertyAsString("key", "X")=="X"); + testOk1(C->getPropertyAsString("TESTKEY", "X")=="value1"); + testOk1(C->getPropertyAsString("OTHERKEY", "X")=="value3"); + setEnv("TESTKEY", "value2"); + setEnv("OTHERKEY","value2"); + testOk1(C->getPropertyAsString("TESTKEY", "X")=="value2"); + testOk1(C->getPropertyAsString("OTHERKEY", "X")=="value3"); +} + static void showAddr(const osiSockAddr& addr) { char buf[40]; @@ -183,8 +201,9 @@ void testConfig() MAIN(configurationTest) { - testPlan(44); + testPlan(49); testProp(); + testBuilder(); testConfig(); return testDone(); } From bcc16bdf00a332df0b615a65efb542df65b92aff Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Dec 2015 08:19:04 -0500 Subject: [PATCH 04/51] serverContext: remove scary leftover --- src/server/serverContext.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 367371e..3931677 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -102,11 +102,6 @@ void ServerContextImpl::initializeLogger() //createFileLogger("serverContextImpl.log"); } -struct noop_deleter -{ - template void operator()(T * p) {} -}; - Configuration::shared_pointer ServerContextImpl::getConfiguration() { Lock guard(_mutex); From b4048c3bfd593fed09c3a197aa656331517fed2a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Dec 2015 12:26:09 -0500 Subject: [PATCH 05/51] blockingUDP: error checking --- src/remote/blockingUDP.h | 2 +- src/remote/blockingUDPTransport.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/remote/blockingUDP.h b/src/remote/blockingUDP.h index 910e5c5..e14cf98 100644 --- a/src/remote/blockingUDP.h +++ b/src/remote/blockingUDP.h @@ -303,7 +303,7 @@ namespace epics { /** * Response handler. */ - std::auto_ptr _responseHandler; + const std::auto_ptr _responseHandler; virtual void processRead(); diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index 483da6f..9723853 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -60,6 +60,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so (serverFlag ? 0x40 : 0x00) | ((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00)) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(blockingUDPTransport); + assert(_responseHandler.get()); osiSocklen_t sockLen = sizeof(sockaddr); // read the actual socket info From 6254525cbaa370e53ec5c515904936d54555e693 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 8 Sep 2015 12:25:19 -0400 Subject: [PATCH 06/51] use epicsThread and Thread::Config Catches errant c++ exceptions and is joinable --- src/remote/blockingTCP.h | 10 ++--- src/remote/blockingTCPAcceptor.cpp | 20 +++------- src/remote/blockingUDP.h | 9 ++--- src/remote/blockingUDPTransport.cpp | 15 +++----- src/remote/codec.cpp | 58 ++++++++++++++--------------- src/remote/codec.h | 12 +++--- 6 files changed, 53 insertions(+), 71 deletions(-) diff --git a/src/remote/blockingTCP.h b/src/remote/blockingTCP.h index cc14af3..1a314da 100644 --- a/src/remote/blockingTCP.h +++ b/src/remote/blockingTCP.h @@ -112,7 +112,7 @@ namespace epics { * @author Matej Sekoranja * @version $Id: BlockingTCPAcceptor.java,v 1.1 2010/05/03 14:45:42 mrkraimer Exp $ */ - class BlockingTCPAcceptor { + class BlockingTCPAcceptor : public epicsThreadRunable { public: POINTER_DEFINITIONS(BlockingTCPAcceptor); @@ -128,8 +128,6 @@ namespace epics { virtual ~BlockingTCPAcceptor(); - void handleEvents(); - /** * Bind socket address. * @return bind socket address, null if not binded. @@ -144,6 +142,8 @@ namespace epics { void destroy(); private: + virtual void run(); + /** * Context instance. */ @@ -176,7 +176,7 @@ namespace epics { epics::pvData::Mutex _mutex; - epicsThreadId _threadId; + epicsThread _thread; /** * Initialize connection acception. @@ -189,8 +189,6 @@ namespace epics { * @return true on success. */ bool validateConnection(Transport::shared_pointer const & transport, const char* address); - - static void handleEventsRunner(void* param); }; } diff --git a/src/remote/blockingTCPAcceptor.cpp b/src/remote/blockingTCPAcceptor.cpp index 32ce611..eb6ea45 100644 --- a/src/remote/blockingTCPAcceptor.cpp +++ b/src/remote/blockingTCPAcceptor.cpp @@ -33,7 +33,10 @@ namespace pvAccess { _serverSocketChannel(INVALID_SOCKET), _receiveBufferSize(receiveBufferSize), _destroyed(false), - _threadId(0) + _thread(*this, "TCP-acceptor", + epicsThreadGetStackSize( + epicsThreadStackMedium), + epicsThreadPriorityMedium) { initialize(port); } @@ -118,14 +121,7 @@ namespace pvAccess { THROW_BASE_EXCEPTION(temp.str().c_str()); } - _threadId - = epicsThreadCreate( - "TCP-acceptor", - epicsThreadPriorityMedium, - epicsThreadGetStackSize( - epicsThreadStackMedium), - BlockingTCPAcceptor::handleEventsRunner, - this); + _thread.start(); // all OK, return return ntohs(_bindAddress.ia.sin_port); @@ -139,7 +135,7 @@ namespace pvAccess { THROW_BASE_EXCEPTION(temp.str().c_str()); } - void BlockingTCPAcceptor::handleEvents() { + void BlockingTCPAcceptor::run() { // rise level if port is assigned dynamically char ipAddrStr[48]; ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr)); @@ -235,10 +231,6 @@ namespace pvAccess { } } - void BlockingTCPAcceptor::handleEventsRunner(void* param) { - ((BlockingTCPAcceptor*)param)->handleEvents(); - } - void BlockingTCPAcceptor::destroy() { Lock guard(_mutex); if(_destroyed) return; diff --git a/src/remote/blockingUDP.h b/src/remote/blockingUDP.h index e14cf98..d8ab0ea 100644 --- a/src/remote/blockingUDP.h +++ b/src/remote/blockingUDP.h @@ -40,7 +40,8 @@ namespace epics { class BlockingUDPTransport : public epics::pvData::NoDefaultMethods, public Transport, public TransportSendControl, - public std::tr1::enable_shared_from_this + public std::tr1::enable_shared_from_this, + public epicsThreadRunable { public: POINTER_DEFINITIONS(BlockingUDPTransport); @@ -305,11 +306,9 @@ namespace epics { */ const std::auto_ptr _responseHandler; - virtual void processRead(); + virtual void run(); private: - static void threadRunner(void* param); - bool processBuffer(Transport::shared_pointer const & transport, osiSockAddr& fromAddress, epics::pvData::ByteBuffer* receiveBuffer); void close(bool waitForThreadToComplete); @@ -374,7 +373,7 @@ namespace epics { /** * Thread ID */ - epicsThreadId _threadId; + std::auto_ptr _thread; epics::pvData::int8 _clientServerWithEndianFlag; diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index 9723853..e735f3e 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -55,7 +55,6 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so _receiveBuffer(new ByteBuffer(MAX_UDP_RECV)), _sendBuffer(new ByteBuffer(MAX_UDP_RECV)), _lastMessageStartPosition(0), - _threadId(0), _clientServerWithEndianFlag( (serverFlag ? 0x40 : 0x00) | ((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00)) { @@ -95,10 +94,10 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so LOG(logLevelTrace, "Starting thread: %s.", threadName.c_str()); } - _threadId = epicsThreadCreate(threadName.c_str(), - epicsThreadPriorityMedium, - epicsThreadGetStackSize(epicsThreadStackSmall), - BlockingUDPTransport::threadRunner, this); + _thread.reset(new epicsThread(*this, threadName.c_str(), + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityMedium)); + _thread->start(); } void BlockingUDPTransport::close() { @@ -203,7 +202,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so _sendBuffer->getPosition()-_lastMessageStartPosition-PVA_MESSAGE_HEADER_SIZE); } - void BlockingUDPTransport::processRead() { + void BlockingUDPTransport::run() { // This function is always called from only one thread - this // object's own thread. @@ -420,10 +419,6 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so return (size_t)sockBufSize; } - void BlockingUDPTransport::threadRunner(void* param) { - ((BlockingUDPTransport*)param)->processRead(); - } - void BlockingUDPTransport::join(const osiSockAddr & mcastAddr, const osiSockAddr & nifAddr) { diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index 4bb1cb5..4b6fee0 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -1027,6 +1027,22 @@ namespace epics { // // + BlockingAbstractCodec::BlockingAbstractCodec( + bool serverFlag, + std::tr1::shared_ptr const & receiveBuffer, + std::tr1::shared_ptr const & sendBuffer, + int32_t socketSendBufferSize) + :AbstractCodec(serverFlag, receiveBuffer, sendBuffer, socketSendBufferSize, true) + ,_readThread(Thread::Config(this, &BlockingAbstractCodec::receiveThread) + .prio(epicsThreadPriorityCAServerLow) + .name("TCP-rx") + .autostart(false)) + ,_sendThread(Thread::Config(this, &BlockingAbstractCodec::sendThread) + .prio(epicsThreadPriorityCAServerLow) + .name("TCP-tx") + .autostart(false)) + { _isOpen.getAndSet(true);} + void BlockingAbstractCodec::readPollOne() { throw std::logic_error("should not be called for blocking IO"); } @@ -1076,35 +1092,21 @@ namespace epics { // NOTE: must not be called from constructor (e.g. needs shared_from_this()) void BlockingAbstractCodec::start() { - _readThread = epicsThreadCreate( - "BlockingAbstractCodec-readThread", - epicsThreadPriorityMedium, - epicsThreadGetStackSize( - epicsThreadStackMedium), - BlockingAbstractCodec::receiveThread, - this); + _readThread.start(); - _sendThread = epicsThreadCreate( - "BlockingAbstractCodec-_sendThread", - epicsThreadPriorityMedium, - epicsThreadGetStackSize( - epicsThreadStackMedium), - BlockingAbstractCodec::sendThread, - this); + _sendThread.start(); } - void BlockingAbstractCodec::receiveThread(void *param) + void BlockingAbstractCodec::receiveThread() { + Transport::shared_pointer ptr = this->shared_from_this(); - BlockingAbstractCodec *bac = static_cast(param); - Transport::shared_pointer ptr = bac->shared_from_this(); - - while (bac->isOpen()) + while (this->isOpen()) { try { - bac->processRead(); + this->processRead(); } catch (std::exception &e) { LOG(logLevelError, "an exception caught while in receiveThread at %s:%d: %s", @@ -1116,22 +1118,20 @@ namespace epics { } } - bac->_shutdownEvent.signal(); + this->_shutdownEvent.signal(); } - void BlockingAbstractCodec::sendThread(void *param) + void BlockingAbstractCodec::sendThread() { + Transport::shared_pointer ptr = this->shared_from_this(); - BlockingAbstractCodec *bac = static_cast(param); - Transport::shared_pointer ptr = bac->shared_from_this(); + this->setSenderThread(); - bac->setSenderThread(); - - while (bac->isOpen()) + while (this->isOpen()) { try { - bac->processWrite(); + this->processWrite(); } catch (connection_closed_exception &cce) { // noop } catch (std::exception &e) { @@ -1155,7 +1155,7 @@ namespace epics { */ // call internal destroy - bac->internalDestroy(); + this->internalDestroy(); } diff --git a/src/remote/codec.h b/src/remote/codec.h index c45174b..a6c88c8 100644 --- a/src/remote/codec.h +++ b/src/remote/codec.h @@ -395,9 +395,7 @@ namespace epics { bool serverFlag, std::tr1::shared_ptr const & receiveBuffer, std::tr1::shared_ptr const & sendBuffer, - int32_t socketSendBufferSize): - AbstractCodec(serverFlag, receiveBuffer, sendBuffer, socketSendBufferSize, true), - _readThread(0), _sendThread(0) { _isOpen.getAndSet(true);} + int32_t socketSendBufferSize); void readPollOne(); void writePollOne(); @@ -408,8 +406,9 @@ namespace epics { bool isOpen(); void start(); - static void receiveThread(void* param); - static void sendThread(void* param); + private: + void receiveThread(); + void sendThread(); protected: void sendBufferFull(int tries); @@ -431,8 +430,7 @@ namespace epics { private: AtomicValue _isOpen; - volatile epicsThreadId _readThread; - volatile epicsThreadId _sendThread; + epics::pvData::Thread _readThread, _sendThread; epics::pvData::Event _shutdownEvent; }; From d68b19e7031018fff448b9d82cd8d019216ba214 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 9 Dec 2015 16:12:42 -0500 Subject: [PATCH 07/51] BlockingTCPAcceptor: smaller listen queue No good can come from making this large. --- src/remote/blockingTCPAcceptor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote/blockingTCPAcceptor.cpp b/src/remote/blockingTCPAcceptor.cpp index eb6ea45..1efeab6 100644 --- a/src/remote/blockingTCPAcceptor.cpp +++ b/src/remote/blockingTCPAcceptor.cpp @@ -112,7 +112,7 @@ namespace pvAccess { } } - retval = ::listen(_serverSocketChannel, 1024); + retval = ::listen(_serverSocketChannel, 4); if(retval<0) { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); ostringstream temp; From 9068fa395005308eb4a413a225cb30005c2bea16 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Dec 2015 20:42:37 -0500 Subject: [PATCH 08/51] ServerContextImpl::setServerPort not fully implemented actually calling this can only break things. --- src/server/serverContext.cpp | 5 ----- src/server/serverContext.h | 6 ------ 2 files changed, 11 deletions(-) diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 3931677..586dd02 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -593,11 +593,6 @@ int32 ServerContextImpl::getServerPort() return _serverPort; } -void ServerContextImpl::setServerPort(int32 port) -{ - _serverPort = port; -} - int32 ServerContextImpl::getBroadcastPort() { return _broadcastPort; diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 61fb5e2..2dc3fa7 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -233,12 +233,6 @@ public: */ epics::pvData::int32 getServerPort(); - /** - * Set server port number. - * @param port new server port number. - */ - void setServerPort(epics::pvData::int32 port); - /** * Get broadcast port. * @return broadcast port. From db86e476597049f523e3be97969b8abe854b303e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 8 Oct 2015 23:00:01 -0400 Subject: [PATCH 09/51] add fair_queue --- src/utils/Makefile | 1 + src/utils/fairQueue.h | 176 ++++++++++++++++++++++++++++++++ testApp/utils/Makefile | 6 +- testApp/utils/testFairQueue.cpp | 75 ++++++++++++++ 4 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 src/utils/fairQueue.h create mode 100644 testApp/utils/testFairQueue.cpp diff --git a/src/utils/Makefile b/src/utils/Makefile index f7e163c..81b16ea 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -11,6 +11,7 @@ INC += referenceCountingLock.h INC += configuration.h INC += likely.h INC += wildcard.h +INC += fairQueue.h LIBSRCS += hexDump.cpp LIBSRCS += inetAddressUtil.cpp diff --git a/src/utils/fairQueue.h b/src/utils/fairQueue.h new file mode 100644 index 0000000..976c14a --- /dev/null +++ b/src/utils/fairQueue.h @@ -0,0 +1,176 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ + +#ifndef FAIRQUEUE_H +#define FAIRQUEUE_H + +#include +#include +#include +#include +#include +#include + +#include + +namespace epics {namespace pvAccess { + + +/** @brief An intrusive, loss-less, unbounded, round-robin queue + * + * The parameterized type 'T' must be a sub-class of @class fair_queue::entry + * + * @li Intrusive. Entries in the queue must derive from @class entry + * + * @li Loss-less. An entry will be returned by pop_front() corresponding to + * each call to push_back(). + * + * @li Un-bounded. There is no upper limit to the number of times an entry + * may be queued other than machine constraints. + * + * @li Round robin. The order that entries are returned may not match + * the order they were added in. "Fairness" is achived by returning + * entries in a rotating fashion based on the order in which they were + * first added. Re-adding the same entry before it is popped does not change + * this order. + * Adding [A, A, B, A, C, C] would give out [A, B, C, A, C, A]. + * + * @warning Only one thread should call pop_front() + * as push_back() does not broadcast (only wakes up one waiter) + */ +template +class epicsShareClass fair_queue +{ + typedef epicsGuard guard_t; +public: + typedef std::tr1::shared_ptr value_type; + + class epicsShareClass entry { + ELLNODE node; + unsigned Qcnt; + value_type holder; +#ifndef NDEBUG + fair_queue *owner; +#endif + + friend class fair_queue; + + entry(const entry&); + entry& operator=(const entry&); + public: + entry() :node(), Qcnt(0), holder() +#ifndef NDEBUG + , owner(NULL) +#endif + { + node.next = node.previous = NULL; + } + ~entry() { + // nodes should be removed from the list before deletion + assert(!node.next && !node.previous && !owner); + } + }; + + fair_queue() + { + ellInit(&list); + } + ~fair_queue() + { + clear(); + assert(ellCount(&list)==0); + } + + void clear() + { + value_type C; + guard_t G(mutex); + do { + pop_front_try(C); + } while(C); + } + + bool empty() const { + guard_t G(mutex); + return ellFirst(&list)==NULL; + } + + void push_back(const value_type& ent) + { + bool wake; + entry *P = ent.get(); + { + guard_t G(mutex); + wake = ellFirst(&list)==NULL; // empty queue + + if(P->Qcnt++==0) { + // not in list + assert(P->owner==NULL); + P->owner = this; + P->holder = ent; // the list will hold a reference + ellAdd(&list, &P->node); // push_back + } else + assert(P->owner==this); + } + if(wake) wakeup.signal(); + } + + bool pop_front_try(value_type& ret) + { + guard_t G(mutex); + ELLNODE *cur = ellGet(&list); // pop_front + + if(cur) { + entry *P = CONTAINER(cur, entry, node); + assert(P->owner==this); + assert(P->Qcnt>0); + if(--P->Qcnt==0) { + P->node.previous = P->node.next = NULL; + P->owner = NULL; + + ret.swap(P->holder); + } else { + ellAdd(&list, &P->node); // push_back + + ret = P->holder; + } + return true; + } else { + ret.reset(); + return false; + } + } + + void pop_front(value_type& ret) + { + while(1) { + pop_front_try(ret); + if(ret) + break; + wakeup.wait(); + } + } + + bool pop_front(value_type& ret, double timeout) + { + while(1) { + pop_front_try(ret); + if(ret) + return true; + if(!wakeup.wait(timeout)) + return false; + } + } + +private: + ELLLIST list; + mutable epicsMutex mutex; + mutable epicsEvent wakeup; +}; + +}} // namespace + +#endif // FAIRQUEUE_H diff --git a/testApp/utils/Makefile b/testApp/utils/Makefile index b4232f0..252455a 100644 --- a/testApp/utils/Makefile +++ b/testApp/utils/Makefile @@ -22,8 +22,6 @@ testInetAddressUtils_SYS_LIBS_WIN32 += ws2_32 testHarness_SRCS += testInetAddressUtils.cpp TESTS += testInetAddressUtils -TESTSCRIPTS_HOST += $(TESTS:%=%.t) - #TESTPROD_HOST += loggerTest #loggerTest_SRCS += loggerTest.cpp #testHarness_SRCS += loggerTest.cpp @@ -49,3 +47,7 @@ configurationTest_SRCS += configurationTest.cpp configurationTest_SYS_LIBS_WIN32 += ws2_32 #testHarness_SRCS += configurationTest.cpp TESTS += configurationTest + +TESTPROD_HOST += testFairQueue +testFairQueue_SRCS += testFairQueue +TESTS += testFairQueue diff --git a/testApp/utils/testFairQueue.cpp b/testApp/utils/testFairQueue.cpp new file mode 100644 index 0000000..6b96c55 --- /dev/null +++ b/testApp/utils/testFairQueue.cpp @@ -0,0 +1,75 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ + +#include + +#include + +#include +#include + +namespace { + +struct Qnode : public epics::pvAccess::fair_queue::entry { + unsigned i; + Qnode(unsigned i):i(i) {} +}; + +} // namespace + +static unsigned Ninput[] = {0,0,0,1,0,2,1,0,1,0,0}; +static unsigned Nexpect[] = {0,1,2,0,1,0,1,0,0,0,0}; + +static +void testOrder() +{ + epics::pvAccess::fair_queue Q; + typedef epics::pvAccess::fair_queue::value_type value_type; + + std::vector unique, inputs, outputs; + unique.resize(3); + unique[0].reset(new Qnode(0)); + unique[1].reset(new Qnode(1)); + unique[2].reset(new Qnode(2)); + + testDiag("Queueing"); + + for(unsigned i=0; ii); + } + } + + testOk(outputs.size()==NELEMENTS(Nexpect), "sizes match actual %u expected %u", + (unsigned)outputs.size(), (unsigned)NELEMENTS(Nexpect)); + + for(unsigned i=0; i=outputs.size()) { + testFail("output truncated"); + continue; + } + testOk(outputs[i]->i==Nexpect[i], "[%u] %u == %u", + i, (unsigned)outputs[i]->i, Nexpect[i]); + } +} + +MAIN(testFairQueue) +{ + testPlan(12); + testOrder(); + return testDone(); +} From 730d30fe5487871a90f28630d1f622bd0fa16e9b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 9 Oct 2015 17:14:17 -0400 Subject: [PATCH 10/51] AbstractCodec use fair_queue --- src/remote/codec.cpp | 33 ++++---- src/remote/codec.h | 121 ++------------------------- src/remote/remote.h | 3 +- testApp/remote/testCodec.cpp | 153 ++++++++++++++++++++++------------- 4 files changed, 120 insertions(+), 190 deletions(-) diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index 4b6fee0..9bb4e6d 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -53,7 +53,7 @@ namespace epics { //PROTECTED _readMode(NORMAL), _version(0), _flags(0), _command(0), _payloadSize(0), _remoteTransportSocketReceiveBufferSize(MAX_TCP_RECV), _totalBytesSent(0), - _blockingProcessQueue(false), _senderThread(0), + _senderThread(0), _writeMode(PROCESS_SEND_QUEUE), _writeOpReady(false),_lowLatency(false), _socketBuffer(receiveBuffer), @@ -98,7 +98,6 @@ namespace epics { _maxSendPayloadSize = _sendBuffer->getSize() - 2*PVA_MESSAGE_HEADER_SIZE; _socketSendBufferSize = socketSendBufferSize; - _blockingProcessQueue = blockingProcessQueue; } @@ -851,7 +850,8 @@ namespace epics { std::size_t senderProcessed = 0; while (senderProcessed++ < MAX_MESSAGE_SEND) { - TransportSender::shared_pointer sender = _sendQueue.take(-1); + TransportSender::shared_pointer sender; + _sendQueue.pop_front_try(sender); if (sender.get() == 0) { // flush @@ -860,19 +860,20 @@ namespace epics { sendCompleted(); // do not schedule sending - if (_blockingProcessQueue) { - if (terminated()) // termination + if (terminated()) // termination break; - sender = _sendQueue.take(0); - // termination (we want to process even if shutdown) - if (sender.get() == 0) - break; - } - else - return; + // termination (we want to process even if shutdown) + _sendQueue.pop_front(sender); } - processSender(sender); + try{ + processSender(sender); + }catch(...){ + if (_sendBuffer->getPosition() > 0) + flush(true); + sendCompleted(); + throw; + } } } @@ -884,13 +885,13 @@ namespace epics { void AbstractCodec::clearSendQueue() { - _sendQueue.clean(); + _sendQueue.clear(); } void AbstractCodec::enqueueSendRequest( TransportSender::shared_pointer const & sender) { - _sendQueue.put(sender); + _sendQueue.push_back(sender); scheduleSend(); } @@ -1066,8 +1067,6 @@ namespace epics { // this is important to avoid cyclic refs (memory leak) clearSendQueue(); - _sendQueue.wakeup(); - // post close internalPostClose(true); } diff --git a/src/remote/codec.h b/src/remote/codec.h index a6c88c8..8d3084d 100644 --- a/src/remote/codec.h +++ b/src/remote/codec.h @@ -112,120 +112,6 @@ namespace epics { #endif - // TODO replace this queue with lock-free implementation - template - class queue { - public: - - queue(void) { } - //TODO - /*queue(queue const &T) = delete; - queue(queue &&T) = delete; - queue& operator=(const queue &T) = delete; - */ - ~queue(void) - { - } - - - bool empty(void) - { - epics::pvData::Lock lock(_queueMutex); - return _queue.empty(); - } - - void clean() - { - epics::pvData::Lock lock(_queueMutex); - _queue.clear(); - } - - - void wakeup() - { - if (!_wakeup.getAndSet(true)) - { - _queueEvent.signal(); - } - } - - - void put(T const & elem) - { - { - epics::pvData::Lock lock(_queueMutex); - _queue.push_back(elem); - } - - _queueEvent.signal(); - } - - - // TODO very sub-optimal (locks and empty() - pop() sequence; at least 2 locks!) - T take(int timeOut) - { - while (true) - { - - bool isEmpty = empty(); - - if (isEmpty) - { - - if (timeOut < 0) { - return T(); - } - - while (isEmpty) - { - - if (timeOut == 0) { - _queueEvent.wait(); - } - else { - _queueEvent.wait(timeOut); - } - - isEmpty = empty(); - if (isEmpty) - { - if (timeOut > 0) { // TODO spurious wakeup, but not critical - return T(); - } - else // if (timeout == 0) cannot be negative - { - if (_wakeup.getAndSet(false)) { - return T(); - } - } - } - } - } - else - { - epics::pvData::Lock lock(_queueMutex); - if (_queue.empty()) - return T(); - T sender = _queue.front(); - _queue.pop_front(); - return sender; - } - } - } - - size_t size() { - epics::pvData::Lock lock(_queueMutex); - return _queue.size(); - } - - private: - - std::deque _queue; - epics::pvData::Event _queueEvent; - epics::pvData::Mutex _queueMutex; - AtomicValue _wakeup; - }; - class epicsShareClass io_exception: public std::runtime_error { public: @@ -327,6 +213,10 @@ namespace epics { char* /*deserializeTo*/, std::size_t /*elementCount*/, std::size_t /*elementSize*/); + bool sendQueueEmpty() const { + return _sendQueue.empty(); + } + protected: virtual void sendBufferFull(int tries) = 0; @@ -341,7 +231,6 @@ namespace epics { int32_t _payloadSize; // TODO why not size_t? epics::pvData::int32 _remoteTransportSocketReceiveBufferSize; int64_t _totalBytesSent; - bool _blockingProcessQueue; //TODO initialize union osiSockAddr _sendTo; epicsThreadId _senderThread; @@ -352,7 +241,7 @@ namespace epics { std::tr1::shared_ptr _socketBuffer; std::tr1::shared_ptr _sendBuffer; - queue _sendQueue; + fair_queue _sendQueue; private: diff --git a/src/remote/remote.h b/src/remote/remote.h index d024fb5..28f7133 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef remoteEpicsExportSharedSymbols # define epicsExportSharedSymbols @@ -142,7 +143,7 @@ namespace epics { /** * Interface defining transport sender (instance sending data over transport). */ - class TransportSender : public Lockable { + class TransportSender : public Lockable, public fair_queue::entry { public: POINTER_DEFINITIONS(TransportSender); diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 250f27d..c476cfe 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -22,6 +22,33 @@ namespace epics { namespace pvAccess { + struct sender_break : public connection_closed_exception + { + sender_break() : connection_closed_exception("break") {} + }; + + struct TransportSenderDisconnect: public TransportSender { + void unlock() {} + void lock() {} + void send(ByteBuffer *buffer, TransportSendControl *control) + { + control->flush(true); + throw sender_break(); + } + }; + + struct TransportSenderSignal: public TransportSender { + Event *evt; + TransportSenderSignal(Event& evt) :evt(&evt) {} + void unlock() {} + void lock() {} + void send(ByteBuffer *buffer, TransportSendControl *control) + { + evt->signal(); + } + }; + + class PVAMessage { public: @@ -257,13 +284,6 @@ namespace epics { } - void endBlockedProcessSendQueue() { - //TODO not thread safe - _blockingProcessQueue = false; - _sendQueue.wakeup(); - } - - void close() { _closedCount++; } bool isOpen() { return _closedCount == 0; } @@ -288,6 +308,10 @@ namespace epics { void sendCompleted() { _sendCompletedCount++; } + void breakSender() { + enqueueSendRequest(std::tr1::shared_ptr(new TransportSenderDisconnect())); + } + bool terminated() { return false; } void cachedSerialize( @@ -412,7 +436,7 @@ namespace epics { public: int runAllTest() { - testPlan(5882); + testPlan(5885); testHeaderProcess(); testInvalidHeaderMagic(); testInvalidHeaderSegmentedInNormal(); @@ -2223,7 +2247,6 @@ namespace epics { "%s: codec._closedCount == 1", CURRENT_FUNCTION); } - class TransportSenderForTestEnqueueSendRequest: public TransportSender { public: @@ -2290,7 +2313,10 @@ namespace epics { // process codec.enqueueSendRequest(sender); codec.enqueueSendRequest(sender2); - codec.processSendQueue(); + codec.breakSender(); + try{ + codec.processSendQueue(); + }catch(sender_break&) {} codec.transferToReadBuffer(); @@ -2430,11 +2456,16 @@ namespace epics { //was processed testOk(0 == codec._sendCompletedCount, "%s: 0 == codec._sendCompletedCount", CURRENT_FUNCTION); + testOk1(!codec.sendQueueEmpty()); - codec.processSendQueue(); + codec.breakSender(); + try{ + codec.processSendQueue(); + }catch(sender_break&) {} testOk(1 == codec._sendCompletedCount, "%s: 1 == codec._sendCompletedCount", CURRENT_FUNCTION); + testOk1(codec.sendQueueEmpty()); codec.transferToReadBuffer(); @@ -2483,6 +2514,13 @@ namespace epics { "%s: 0 == codec.getSendBuffer()->getPosition()", CURRENT_FUNCTION); + testOk1(codec.sendQueueEmpty()); + + testDiag("%u %u", (unsigned)codec._scheduleSendCount, + (unsigned)codec._sendCompletedCount); + testOk1(3 == codec._scheduleSendCount); + testOk1(1 == codec._sendCompletedCount); + // now queue is empty and thread is right codec.enqueueSendRequest(sender2, PVA_MESSAGE_HEADER_SIZE); @@ -2491,12 +2529,17 @@ namespace epics { "%s: PVA_MESSAGE_HEADER_SIZE == " "codec.getSendBuffer()->getPosition()", CURRENT_FUNCTION); - testOk(3 == codec._scheduleSendCount, - "%s: 3 == codec._scheduleSendCount", CURRENT_FUNCTION); - testOk(1 == codec._sendCompletedCount, - "%s: 1 == codec._sendCompletedCount", CURRENT_FUNCTION); - codec.processWrite(); + testDiag("%u %u", (unsigned)codec._scheduleSendCount, + (unsigned)codec._sendCompletedCount); + testOk1(4 == codec._scheduleSendCount); + testOk1(1 == codec._sendCompletedCount); + + codec.breakSender(); + + try{ + codec.processWrite(); + }catch(sender_break&) {} testOk(2 == codec._sendCompletedCount, "%s: 2 == codec._sendCompletedCount", CURRENT_FUNCTION); @@ -2575,7 +2618,10 @@ namespace epics { // process codec.enqueueSendRequest(sender); - codec.processSendQueue(); + codec.breakSender(); + try{ + codec.processSendQueue(); + }catch(sender_break&) {} codec.transferToReadBuffer(); @@ -2664,7 +2710,7 @@ namespace epics { // process codec.enqueueSendRequest(sender); - + codec.breakSender(); try { codec.processSendQueue(); @@ -2781,7 +2827,10 @@ namespace epics { // process codec.enqueueSendRequest(sender); - codec.processSendQueue(); + codec.breakSender(); + try{ + codec.processSendQueue(); + }catch(sender_break&) {} codec.addToReadBuffer(); @@ -2906,7 +2955,10 @@ namespace epics { codec.clearSendQueue(); - codec.processSendQueue(); + codec.breakSender(); + try{ + codec.processSendQueue(); + }catch(sender_break&) {} testOk(0 == codec.getSendBuffer()->getPosition(), "%s: 0 == codec.getSendBuffer()->getPosition()", @@ -3128,6 +3180,7 @@ namespace epics { TransportSendControl* control) { _codec.putControlMessage((int8_t)0x01, 0x00112233); + _codec.flush(true); } private: @@ -3135,16 +3188,20 @@ namespace epics { }; - class ValueHolder { + class ValueHolder : public Runnable { public: - ValueHolder( - TestCodec &testCodec, - AtomicValue &processTreadExited): - _testCodec(testCodec), - _processTreadExited(processTreadExited) {} + ValueHolder(TestCodec &testCodec): + _testCodec(testCodec) {} TestCodec &_testCodec; - AtomicValue & _processTreadExited; + Event waiter; + + virtual void run() { + waiter.signal(); + try{ + _testCodec.processSendQueue(); + }catch(sender_break&) {} + } }; @@ -3155,56 +3212,40 @@ namespace epics { TestCodec codec(DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE, true); - _processTreadExited.getAndSet(false); std::tr1::shared_ptr sender = std::tr1::shared_ptr( new TransportSenderForTestBlockingProcessQueueTest(codec)); - ValueHolder valueHolder(codec, _processTreadExited); + ValueHolder valueHolder(codec); + Event done; - epicsThreadCreate( - "testBlockingProcessQueueTest-processThread", - epicsThreadPriorityMedium, - epicsThreadGetStackSize( - epicsThreadStackMedium), - CodecTest::blockingProcessQueueThread, - &valueHolder); + Thread thr(Thread::Config(&valueHolder) + .name("testBlockingProcessQueueTest-processThread")); - epicsThreadSleep(3); - - testOk(_processTreadExited.get() == false, - "%s: _processTreadExited.get() == false", - CURRENT_FUNCTION); + valueHolder.waiter.wait(); // let's put something into it codec.enqueueSendRequest(sender); + codec.enqueueSendRequest(std::tr1::shared_ptr(new TransportSenderSignal(done))); - epicsThreadSleep(1); + testDiag("Waiting for work"); + done.wait(); testOk((std::size_t)PVA_MESSAGE_HEADER_SIZE == codec._writeBuffer.getPosition(), "%s: PVA_MESSAGE_HEADER_SIZE == " - "codec._writeBuffer.getPosition()", - CURRENT_FUNCTION); + "codec._writeBuffer.getPosition() (%u)", + CURRENT_FUNCTION, + (unsigned)codec._writeBuffer.getPosition()); - codec.endBlockedProcessSendQueue(); + codec.breakSender(); - epicsThreadSleep(1); - - testOk(_processTreadExited.get() == true, - "%s: _processTreadExited.get() == true", CURRENT_FUNCTION); + thr.exitWait(); } private: - void static blockingProcessQueueThread(void *param) { - ValueHolder *valueHolder = static_cast(param); - // this should block - valueHolder->_testCodec.processSendQueue(); - valueHolder->_processTreadExited.getAndSet(true); - } - AtomicValue _processTreadExited; }; } From f2b47ef5e94e896b795bbd1bd5b767ecae7c80d6 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 18 Nov 2015 14:12:48 -0600 Subject: [PATCH 11/51] thread shutdown --- src/remote/blockingTCPAcceptor.cpp | 29 ++++++++++++++++++++++++----- src/server/serverContext.cpp | 4 ++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/remote/blockingTCPAcceptor.cpp b/src/remote/blockingTCPAcceptor.cpp index 1efeab6..ae75275 100644 --- a/src/remote/blockingTCPAcceptor.cpp +++ b/src/remote/blockingTCPAcceptor.cpp @@ -232,16 +232,35 @@ namespace pvAccess { } void BlockingTCPAcceptor::destroy() { - Lock guard(_mutex); - if(_destroyed) return; - _destroyed = true; + SOCKET sock; + { + Lock guard(_mutex); + if(_destroyed) return; + _destroyed = true; - if(_serverSocketChannel!=INVALID_SOCKET) { + sock = _serverSocketChannel; + _serverSocketChannel = INVALID_SOCKET; + } + + if(sock!=INVALID_SOCKET) { char ipAddrStr[48]; ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr)); LOG(logLevelDebug, "Stopped accepting connections at %s.", ipAddrStr); - epicsSocketDestroy(_serverSocketChannel); + switch(epicsSocketSystemCallInterruptMechanismQuery()) + { + case esscimqi_socketBothShutdownRequired: + shutdown(sock, SHUT_RDWR); + _thread.exitWait(); + epicsSocketDestroy(sock); + break; + case esscimqi_socketSigAlarmRequired: + LOG(logLevelError, "SigAlarm close not implemented for this target\n"); + case esscimqi_socketCloseRequired: + epicsSocketDestroy(sock); + _thread.exitWait(); + break; + } } } diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 586dd02..e1a02a9 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -545,6 +545,10 @@ void ServerContextImpl::dispose() { destroy(); } + catch(std::exception& e) + { + std::cerr<<"Error in: ServerContextImpl::dispose: "< Date: Wed, 18 Nov 2015 16:20:49 -0600 Subject: [PATCH 12/51] Remove clearSendQueue Use BreakTransport exception instead --- src/remote/codec.cpp | 56 +++++++++++++++----------- src/remote/codec.h | 2 +- src/remoteClient/clientContextImpl.cpp | 5 ++- testApp/remote/testCodec.cpp | 37 +---------------- 4 files changed, 38 insertions(+), 62 deletions(-) diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index 9bb4e6d..7036d1a 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -32,6 +32,18 @@ using namespace std; using namespace epics::pvData; using namespace epics::pvAccess; +namespace { +struct BreakTransport : TransportSender +{ + virtual ~BreakTransport() {} + virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) + { + throw epics::pvAccess::detail::connection_closed_exception("Break"); + } + virtual void lock() {} + virtual void unlock() {} +}; +} // namespace namespace epics { namespace pvAccess { @@ -883,12 +895,6 @@ namespace epics { } - void AbstractCodec::clearSendQueue() - { - _sendQueue.clear(); - } - - void AbstractCodec::enqueueSendRequest( TransportSender::shared_pointer const & sender) { _sendQueue.push_back(sender); @@ -1044,6 +1050,13 @@ namespace epics { .autostart(false)) { _isOpen.getAndSet(true);} + BlockingAbstractCodec::~BlockingAbstractCodec() + { + assert(!_isOpen.get()); + _sendThread.exitWait(); + _readThread.exitWait(); + } + void BlockingAbstractCodec::readPollOne() { throw std::logic_error("should not be called for blocking IO"); } @@ -1061,18 +1074,21 @@ namespace epics { // always close in the same thread, same way, etc. // wakeup processSendQueue - // clean resources + // clean resources (close socket) internalClose(true); - // this is important to avoid cyclic refs (memory leak) - clearSendQueue(); + // Break sender from queue wait + BreakTransport::shared_pointer B(new BreakTransport); + enqueueSendRequest(B); // post close internalPostClose(true); } } - void BlockingAbstractCodec::internalClose(bool /*force*/) { + void BlockingAbstractCodec::internalClose(bool /*force*/) + { + this->internalDestroy(); } void BlockingAbstractCodec::internalPostClose(bool /*force*/) { @@ -1143,18 +1159,7 @@ namespace epics { __FILE__, __LINE__); } } - - /* - // wait read thread to die - // TODO rewise if this is really needed - // this timeout is needed where close() is initiated from the send thread, - // and not from the read thread as usualy - recv() does not exit until socket is not destroyed, - // which is done the internalDestroy() call below - bac->_shutdownEvent.wait(3.0); - */ - - // call internal destroy - this->internalDestroy(); + _sendQueue.clear(); } @@ -1233,7 +1238,7 @@ namespace epics { epicsSocketDestroy(_channel); } - _channel = INVALID_SOCKET; + _channel = INVALID_SOCKET; //TODO: mutex to guard _channel } } @@ -1863,7 +1868,10 @@ namespace epics { // not used anymore, close it // TODO consider delayed destruction (can improve performance!!!) - if(_owners.size()==0) close(); // TODO close(false) + if(_owners.size()==0) { + lock.unlock(); + close(); + } } void BlockingClientTCPTransportCodec::aliveNotification() { diff --git a/src/remote/codec.h b/src/remote/codec.h index 8d3084d..e01d863 100644 --- a/src/remote/codec.h +++ b/src/remote/codec.h @@ -193,7 +193,6 @@ namespace epics { void processWrite(); void processRead(); void processSendQueue(); - void clearSendQueue(); void enqueueSendRequest(TransportSender::shared_pointer const & sender); void enqueueSendRequest(TransportSender::shared_pointer const & sender, std::size_t requiredBufferSize); @@ -285,6 +284,7 @@ namespace epics { std::tr1::shared_ptr const & receiveBuffer, std::tr1::shared_ptr const & sendBuffer, int32_t socketSendBufferSize); + virtual ~BlockingAbstractCodec(); void readPollOne(); void writePollOne(); diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index ffe72bb..84a2e66 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -3877,6 +3877,9 @@ namespace epics { * @param remoteDestroy issue channel destroy request. */ void disconnect(bool initiateSearch, bool remoteDestroy) { + // order of oldchan and guard is important to ensure + // oldchan is destoryed after unlock + Transport::shared_pointer oldchan; Lock guard(m_channelMutex); if (m_connectionState != CONNECTED) @@ -3900,7 +3903,7 @@ namespace epics { } m_transport->release(getID()); - m_transport.reset(); + oldchan.swap(m_transport); } if (initiateSearch) diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index c476cfe..9afa90d 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -436,7 +436,7 @@ namespace epics { public: int runAllTest() { - testPlan(5885); + testPlan(5883); testHeaderProcess(); testInvalidHeaderMagic(); testInvalidHeaderSegmentedInNormal(); @@ -462,7 +462,6 @@ namespace epics { testSendException(); testSendHugeMessagePartes(); testRecipient(); - testClearSendQueue(); testInvalidArguments(); testDefaultModes(); testEnqueueSendRequestExceptionThrown(); @@ -2935,40 +2934,6 @@ namespace epics { TestCodec &_codec; }; - - void testClearSendQueue() - { - testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); - TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); - - std::tr1::shared_ptr sender = - std::tr1::shared_ptr( - new TransportSenderForTestClearSendQueue(codec)); - - std::tr1::shared_ptr sender2 = - std::tr1::shared_ptr( - new TransportSender2ForTestClearSendQueue(codec)); - - - codec.enqueueSendRequest(sender); - codec.enqueueSendRequest(sender2); - - codec.clearSendQueue(); - - codec.breakSender(); - try{ - codec.processSendQueue(); - }catch(sender_break&) {} - - testOk(0 == codec.getSendBuffer()->getPosition(), - "%s: 0 == codec.getSendBuffer()->getPosition()", - CURRENT_FUNCTION); - testOk(0 == codec._writeBuffer.getPosition(), - "%s: 0 == codec._writeBuffer.getPosition()", - CURRENT_FUNCTION); - } - - void testInvalidArguments() { testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); From b9dd9e8e6c40a058b16410e3a4f516dff07d0fb6 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 8 Dec 2015 22:07:26 -0500 Subject: [PATCH 13/51] getRemoteName() Cache the TCP peer address as a string and pass it to createChannel(). --- src/remote/blockingUDP.h | 5 +++++ src/remote/blockingUDPTransport.cpp | 5 +++++ src/remote/codec.cpp | 35 +++++++++++------------------ src/remote/codec.h | 4 ++++ src/remote/remote.h | 2 ++ src/server/responseHandlers.cpp | 2 +- testApp/remote/testCodec.cpp | 2 ++ 7 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/remote/blockingUDP.h b/src/remote/blockingUDP.h index d8ab0ea..554204b 100644 --- a/src/remote/blockingUDP.h +++ b/src/remote/blockingUDP.h @@ -73,6 +73,10 @@ namespace epics { return &_remoteAddress; } + virtual const std::string& getRemoteName() const { + return _remoteName; + } + virtual std::string getType() const { return std::string("udp"); } @@ -329,6 +333,7 @@ namespace epics { * Remote address. */ osiSockAddr _remoteAddress; + std::string _remoteName; /** * Send addresses. diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index e735f3e..36dabdc 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -71,6 +71,11 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so char strBuffer[64]; epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); LOG(logLevelDebug, "getsockname error: %s.", strBuffer); + _remoteName = ":0"; + } else { + char strBuffer[64]; + sockAddrToA(&_remoteAddress.sa, strBuffer, sizeof(strBuffer)); + _remoteName = strBuffer; } } diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index 7036d1a..f886c0a 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -1201,7 +1201,13 @@ namespace epics { LOG(logLevelError, "Error fetching socket remote address: %s.", errStr); + _socketName = ":0"; + } else { + char ipAddrStr[64]; + ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); + _socketName = ipAddrStr; } + } // must be called only once, when there will be no operation on socket (e.g. just before tx/rx thread exists) @@ -1443,7 +1449,6 @@ namespace epics { sendBufferSize, receiveBufferSize, PVA_DEFAULT_PRIORITY), _lastChannelSID(0), _verifyOrVerified(false), _securityRequired(false) { - // NOTE: priority not yet known, default priority is used to //register/unregister // TODO implement priorities in Reactor... not that user will @@ -1589,12 +1594,10 @@ namespace epics { if (IS_LOGGABLE(logLevelDebug)) { - char ipAddrStr[64]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); LOG( logLevelDebug, "Transport to %s still has %zd channel(s) active and closing...", - ipAddrStr, _channels.size()); + _socketName.c_str(), _channels.size()); } std::map::iterator it = _channels.begin(); @@ -1614,9 +1617,7 @@ namespace epics { { if (IS_LOGGABLE(logLevelDebug)) { - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG(logLevelDebug, "Authentication completed with status '%s' for PVA client: %s.", Status::StatusTypeName[status.getType()], ipAddrStr); + LOG(logLevelDebug, "Authentication completed with status '%s' for PVA client: %s.", Status::StatusTypeName[status.getType()], _socketName.c_str()); } if (!isVerified()) // TODO sync @@ -1663,9 +1664,7 @@ namespace epics { if (IS_LOGGABLE(logLevelDebug)) { - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG(logLevelDebug, "No security plug-in installed, selecting default plug-in '%s' for PVA client: %s.", securityPlugin->getId().c_str(), ipAddrStr); + LOG(logLevelDebug, "No security plug-in installed, selecting default plug-in '%s' for PVA client: %s.", securityPlugin->getId().c_str(), _socketName.c_str()); } } } @@ -1689,9 +1688,7 @@ namespace epics { } catch (SecurityException &se) { if (IS_LOGGABLE(logLevelDebug)) { - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG(logLevelDebug, "Security plug-in '%s' failed to create a session for PVA client: %s.", initData->securityPluginName.c_str(), ipAddrStr); + LOG(logLevelDebug, "Security plug-in '%s' failed to create a session for PVA client: %s.", initData->securityPluginName.c_str(), _socketName.c_str()); } Status status(Status::STATUSTYPE_ERROR, se.what()); verified(status); @@ -1792,9 +1789,7 @@ namespace epics { if (IS_LOGGABLE(logLevelDebug)) { - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG(logLevelDebug, "Acquiring transport to %s.", ipAddrStr); + LOG(logLevelDebug, "Acquiring transport to %s.", _socketName.c_str()); } _owners[client->getID()] = TransportClient::weak_pointer(client); @@ -1829,12 +1824,10 @@ namespace epics { if (IS_LOGGABLE(logLevelDebug)) { - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); LOG( logLevelDebug, "Transport to %s still has %d client(s) active and closing...", - ipAddrStr, refs); + _socketName.c_str(), refs); } TransportClientMap_t::iterator it = _owners.begin(); @@ -1858,9 +1851,7 @@ namespace epics { if (IS_LOGGABLE(logLevelDebug)) { - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG(logLevelDebug, "Releasing TCP transport to %s.", ipAddrStr); + LOG(logLevelDebug, "Releasing TCP transport to %s.", _socketName.c_str()); } _owners.erase(clientID); diff --git a/src/remote/codec.h b/src/remote/codec.h index e01d863..07a7410 100644 --- a/src/remote/codec.h +++ b/src/remote/codec.h @@ -348,6 +348,7 @@ namespace epics { SOCKET _channel; osiSockAddr _socketAddress; + std::string _socketName; }; @@ -393,6 +394,9 @@ namespace epics { return &_socketAddress; } + const std::string& getRemoteName() const { + return _socketName; + } epics::pvData::int8 getRevision() const { return PVA_PROTOCOL_REVISION; diff --git a/src/remote/remote.h b/src/remote/remote.h index 28f7133..c05306e 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -199,6 +199,8 @@ namespace epics { */ virtual const osiSockAddr* getRemoteAddress() const = 0; + virtual const std::string& getRemoteName() const = 0; + // TODO getContext? /** diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index b6fe912..d1db8c0 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -760,7 +760,7 @@ ChannelRequester::shared_pointer ServerChannelRequesterImpl::create( std::tr1::shared_ptr tp(new ServerChannelRequesterImpl(transport, channelName, cid, css)); ChannelRequester::shared_pointer cr = tp; // TODO exception guard and report error back - provider->createChannel(channelName, cr, transport->getPriority()); + provider->createChannel(channelName, cr, transport->getPriority(), transport->getRemoteName()); return cr; } diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 9afa90d..77115de 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -350,6 +350,8 @@ namespace epics { } const osiSockAddr* getRemoteAddress() const { return 0; } + std::string dummyRemoteName; + const std::string& getRemoteName() const {return dummyRemoteName;} epics::pvData::int8 getRevision() const { From 03e5b0c747e07dbafb5541d3759eb8479baba96c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Nov 2015 17:06:58 -0500 Subject: [PATCH 14/51] Allow ServerContextImpl to be created w/ specific conf. --- src/server/serverContext.cpp | 13 +++++++++++-- src/server/serverContext.h | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index e1a02a9..a747670 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -51,13 +51,22 @@ ServerContextImpl::ServerContextImpl(): epicsSignalInstallSigPipeIgnore (); generateGUID(); - initializeLogger(); - loadConfiguration(); + initializeLogger(); } ServerContextImpl::shared_pointer ServerContextImpl::create() { ServerContextImpl::shared_pointer thisPointer(new ServerContextImpl()); + thisPointer->loadConfiguration(); + return thisPointer; +} + +ServerContextImpl::shared_pointer ServerContextImpl::create( + const Configuration::shared_pointer& conf) +{ + ServerContextImpl::shared_pointer thisPointer(new ServerContextImpl()); + thisPointer->configuration = conf; + thisPointer->loadConfiguration(); return thisPointer; } diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 2dc3fa7..80a3987 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -116,10 +116,11 @@ class epicsShareClass ServerContextImpl : public: typedef std::tr1::shared_ptr shared_pointer; typedef std::tr1::shared_ptr const_shared_pointer; -protected: +private: ServerContextImpl(); public: static shared_pointer create(); + static shared_pointer create(const Configuration::shared_pointer& conf); virtual ~ServerContextImpl(); From c0ee432598eccaeb46dab78194cf33ac5a0e38f1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Dec 2015 17:31:18 -0500 Subject: [PATCH 15/51] Allow ChannelProviderFactory::newInstance to accept a Configuration Deprecate ChannelProvider::configure(), which doesn't do much and is incompatible with the idea of shared context. A lot of down-stream mess related to the confused relationship between InternalClientContextImpl and InternalClientContextImpl::ChannelProviderImpl. This is changed to compose the provider within the context and use a nested shared_ptr so that references to the provider are really references to the context. This brings the ownership semantic in line with what the API suggests, and what other providers implement. --- src/client/pvAccess.h | 19 ++- src/pva/clientFactory.cpp | 70 ++++------- src/remoteClient/clientContextImpl.cpp | 159 ++++++++---------------- src/remoteClient/clientContextImpl.h | 6 +- testApp/remote/testRemoteClientImpl.cpp | 11 -- 5 files changed, 95 insertions(+), 170 deletions(-) diff --git a/src/client/pvAccess.h b/src/client/pvAccess.h index c8afdf0..f7f275c 100644 --- a/src/client/pvAccess.h +++ b/src/client/pvAccess.h @@ -32,6 +32,7 @@ namespace epics { namespace pvAccess { + class Configuration; // TODO add write-only? // change names @@ -870,7 +871,7 @@ namespace pvAccess { virtual Channel::shared_pointer createChannel(std::string const & channelName,ChannelRequester::shared_pointer const & channelRequester, short priority, std::string const & address) = 0; - virtual void configure(epics::pvData::PVStructure::shared_pointer /*configuration*/) {}; + virtual void configure(epics::pvData::PVStructure::shared_pointer /*configuration*/) EPICS_DEPRECATED {}; virtual void flush() {}; virtual void poll() {}; @@ -892,16 +893,26 @@ namespace pvAccess { virtual std::string getFactoryName() = 0; /** - * Get a shared instance. + * Get a shared instance using the default Configuration. * @return a shared instance. */ virtual ChannelProvider::shared_pointer sharedInstance() = 0; /** - * Create a new instance. + * Create a new instance using the default Configuration. * @return a new instance. */ - virtual ChannelProvider::shared_pointer newInstance() = 0; + virtual ChannelProvider::shared_pointer newInstance() { + return newInstance(std::tr1::shared_ptr()); + } + + /** + * Create a new instance using a specific Configuration. + * @return a new instance. + */ + virtual ChannelProvider::shared_pointer newInstance(const std::tr1::shared_ptr&) { + throw std::logic_error("This ChannelProviderFactory does not support non-default configurations"); + } }; /** diff --git a/src/pva/clientFactory.cpp b/src/pva/clientFactory.cpp index abac587..3157b91 100644 --- a/src/pva/clientFactory.cpp +++ b/src/pva/clientFactory.cpp @@ -16,9 +16,8 @@ using namespace epics::pvData; using namespace epics::pvAccess; -// TODO global static variable (de/initialization order not guaranteed) -static Mutex mutex; -static ClientContextImpl::shared_pointer context; +static Mutex cfact_mutex; +static ChannelProvider::shared_pointer cfact_shared_provider; class ChannelProviderFactoryImpl : public ChannelProviderFactory { @@ -32,70 +31,47 @@ public: virtual ChannelProvider::shared_pointer sharedInstance() { - Lock guard(mutex); - if (!context.get()) + Lock guard(cfact_mutex); + if (!cfact_shared_provider) { - try { - ClientContextImpl::shared_pointer lcontext = createClientContextImpl(); - lcontext->initialize(); - context = lcontext; - } catch (std::exception &e) { - LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what()); - } catch (...) { - LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__); - } + Configuration::shared_pointer def; + cfact_shared_provider = createClientProvider(def); } - return context->getProvider(); + return cfact_shared_provider; } - virtual ChannelProvider::shared_pointer newInstance() + virtual ChannelProvider::shared_pointer newInstance(const std::tr1::shared_ptr& conf) { - Lock guard(mutex); - try { - ClientContextImpl::shared_pointer lcontext = createClientContextImpl(); - lcontext->initialize(); - return lcontext->getProvider(); - } catch (std::exception &e) { - LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what()); - return ChannelProvider::shared_pointer(); - } catch (...) { - LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__); - return ChannelProvider::shared_pointer(); - } - } - - void destroySharedInstance() - { - Lock guard(mutex); - if (context.get()) - { - context->dispose(); - context.reset(); - } + Lock guard(cfact_mutex); + return createClientProvider(conf); } }; -static ChannelProviderFactoryImpl::shared_pointer factory; +static ChannelProviderFactoryImpl::shared_pointer pva_factory; void ClientFactory::start() { epicsSignalInstallSigAlarmIgnore(); epicsSignalInstallSigPipeIgnore(); - Lock guard(mutex); - if (!factory.get()) - factory.reset(new ChannelProviderFactoryImpl()); + Lock guard(cfact_mutex); + if (!pva_factory) + pva_factory.reset(new ChannelProviderFactoryImpl()); - registerChannelProviderFactory(factory); + registerChannelProviderFactory(pva_factory); } void ClientFactory::stop() { - Lock guard(mutex); + Lock guard(cfact_mutex); - if (factory.get()) + if (pva_factory) { - unregisterChannelProviderFactory(factory); - factory->destroySharedInstance(); + unregisterChannelProviderFactory(pva_factory); + if(!pva_factory.unique()) { + LOG(logLevelWarn, "ClientFactory::stop() finds shared client context with %u remaining users", + (unsigned)pva_factory.use_count()); + } + pva_factory.reset(); } } diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 84a2e66..a64dfdc 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -3269,45 +3269,12 @@ namespace epics { public ClientContextImpl, public std::tr1::enable_shared_from_this { - - - class ChannelProviderImpl; -/* - class ChannelImplFind : public ChannelFind - { - public: - ChannelImplFind(ChannelProvider::shared_pointer const & provider) : m_provider(provider) - { - } - - virtual void destroy() - { - // one instance for all, do not delete at all - } - - virtual ChannelProvider::shared_pointer getChannelProvider() - { - return m_provider; - }; - - virtual void cancel() - { - throw std::runtime_error("not supported"); - } - - private: - - // only to be destroyed by it - friend class ChannelProviderImpl; - virtual ~ChannelImplFind() {} - - ChannelProvider::shared_pointer m_provider; - }; -*/ + public: + POINTER_DEFINITIONS(InternalClientContextImpl); class ChannelProviderImpl : public ChannelProvider { public: - ChannelProviderImpl(std::tr1::shared_ptr const & context) : + ChannelProviderImpl(InternalClientContextImpl* context) : m_context(context) { MB_INIT; @@ -3328,9 +3295,7 @@ namespace epics { { // TODO not implemented - std::tr1::shared_ptr context = m_context.lock(); - if (context.get()) - context->checkChannelName(channelName); + m_context->checkChannelName(channelName); if (!channelFindRequester.get()) throw std::runtime_error("null requester"); @@ -3368,15 +3333,6 @@ namespace epics { short priority, std::string const & addressesStr) { - std::tr1::shared_ptr context = m_context.lock(); - if (!context.get()) - { - Status errorStatus(Status::STATUSTYPE_ERROR, "context already destroyed"); - Channel::shared_pointer nullChannel; - EXCEPTION_GUARD(channelRequester->channelCreated(errorStatus, nullChannel)); - return nullChannel; - } - auto_ptr addresses; if (!addressesStr.empty()) { @@ -3385,7 +3341,7 @@ namespace epics { addresses.reset(); } - Channel::shared_pointer channel = context->createChannelInternal(channelName, channelRequester, priority, addresses); + Channel::shared_pointer channel = m_context->createChannelInternal(channelName, channelRequester, priority, addresses); if (channel.get()) channelRequester->channelCreated(Status::Ok, channel); return channel; @@ -3395,37 +3351,31 @@ namespace epics { virtual void configure(epics::pvData::PVStructure::shared_pointer configuration) { - std::tr1::shared_ptr context = m_context.lock(); - if (context.get()) - context->configure(configuration); + LOG(logLevelError, "Client context must be configured at construction (see ChannelProvider::newInstance(conf))"); } virtual void flush() { - std::tr1::shared_ptr context = m_context.lock(); - if (context.get()) - context->flush(); + m_context->flush(); } virtual void poll() { - std::tr1::shared_ptr context = m_context.lock(); - if (context.get()) - context->poll(); + m_context->poll(); } ~ChannelProviderImpl() {}; private: - std::tr1::weak_ptr m_context; + InternalClientContextImpl* const m_context; }; - + private: /** * Implementation of PVAJ JCA Channel. */ @@ -3439,7 +3389,7 @@ namespace epics { /** * Context. */ - ClientContextImpl::shared_pointer m_context; + std::tr1::shared_ptr m_context; /** * Client channel ID. @@ -3528,7 +3478,7 @@ namespace epics { * @throws PVAException */ InternalChannelImpl( - ClientContextImpl::shared_pointer const & context, + InternalClientContextImpl::shared_pointer const & context, pvAccessID channelID, string const & name, ChannelRequester::shared_pointer const & requester, @@ -3562,7 +3512,7 @@ namespace epics { public: - static ChannelImpl::shared_pointer create(ClientContextImpl::shared_pointer context, + static ChannelImpl::shared_pointer create(InternalClientContextImpl::shared_pointer context, pvAccessID channelID, string const & name, ChannelRequester::shared_pointer requester, @@ -3602,7 +3552,7 @@ namespace epics { virtual ChannelProvider::shared_pointer getProvider() { - return m_context->getProvider(); + return ChannelProvider::shared_pointer(m_context->m_provider_ptr); } // NOTE: synchronization guarantees that transport is non-0 and state == CONNECTED. @@ -3610,12 +3560,11 @@ namespace epics { { Lock guard(m_channelMutex); if (m_connectionState != CONNECTED) { - static string emptyString; - return emptyString; + return ""; } else { - return inetAddressToString(*m_transport->getRemoteAddress()); + return m_transport->getRemoteName(); } } @@ -4302,11 +4251,9 @@ namespace epics { - + public: - private: - - InternalClientContextImpl() : + InternalClientContextImpl(const Configuration::shared_pointer& conf) : m_addressList(""), m_autoAddressList(true), m_connectionTimeout(30.0f), m_beaconPeriod(15.0f), m_broadcastPort(PVA_BROADCAST_PORT), m_receiveBufferSize(MAX_TCP_RECV), m_namedLocker(), m_lastCID(0), m_lastIOID(0), @@ -4315,40 +4262,18 @@ namespace epics { EPICS_PVA_MINOR_VERSION, EPICS_PVA_MAINTENANCE_VERSION, EPICS_PVA_DEVELOPMENT_FLAG), + m_provider(this), m_contextState(CONTEXT_NOT_INITIALIZED), - m_configuration(new SystemConfigurationImpl()), + m_configuration(conf), m_flushStrategy(DELAYED) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(remoteClientContext); + if(!m_configuration) m_configuration = ConfigurationFactory::getConfiguration("pvAccess-client"); m_flushTransports.reserve(64); loadConfiguration(); } - public: - - static shared_pointer create() - { - // TODO use std::make_shared - std::tr1::shared_ptr tp(new InternalClientContextImpl(), delayed_destroyable_deleter()); - shared_pointer thisPointer = tp; - static_cast(thisPointer.get())->activate(); - return thisPointer; - } - - void activate() - { - m_provider.reset(new ChannelProviderImpl(shared_from_this())); - } - virtual Configuration::shared_pointer getConfiguration() { - /* -TODO - final ConfigurationProvider configurationProvider = ConfigurationFactory.getProvider(); - Configuration config = configurationProvider.getConfiguration("pvAccess-client"); - if (!config) - config = configurationProvider.getConfiguration("system"); - return config; -*/ return m_configuration; } @@ -4356,10 +4281,6 @@ TODO return m_version; } - virtual ChannelProvider::shared_pointer const & getProvider() { - return m_provider; - } - virtual Timer::shared_pointer getTimer() { return m_timer; @@ -4916,7 +4837,7 @@ TODO } virtual void configure(epics::pvData::PVStructure::shared_pointer configuration) - { + {// remove? if (m_transportRegistry->numberOfActiveTransports() > 0) throw std::runtime_error("Configure must be called when there is no transports active."); @@ -5091,10 +5012,13 @@ TODO */ Version m_version; + public: /** * Provider implementation. */ - ChannelProviderImpl::shared_pointer m_provider; + ChannelProviderImpl m_provider; + ChannelProviderImpl::weak_pointer m_provider_ptr; + private: /** * Context state. @@ -5117,10 +5041,35 @@ TODO osiSockAddr m_localBroadcastAddress; }; - ClientContextImpl::shared_pointer createClientContextImpl() + namespace { + struct nested { + ClientContextImpl::shared_pointer ptr; + nested(const ClientContextImpl::shared_pointer& p) :ptr(p) {} + void operator()(ChannelProvider*) { + ptr.reset(); + } + }; + } + + ChannelProvider::shared_pointer createClientProvider(const Configuration::shared_pointer& conf) { - ClientContextImpl::shared_pointer ptr = InternalClientContextImpl::create(); - return ptr; + /* For PVA the context and provider have a tight 1-to-1 relationship + * were each must know about the other, and both must be referenced by shared_ptr + * from outside code (provider) and Channels (context and provider. + * + * So we compose the provider within the context and use a nested shared_ptr + * to the context for the provider. + */ + ClientContextImpl::shared_pointer ctxt(new InternalClientContextImpl(conf)); + + InternalClientContextImpl *self = static_cast(ctxt.get()); + + // a wrapped shared_ptr to the provider which really holds a reference to the context + ChannelProvider::shared_pointer prov(&self->m_provider, nested(ctxt)); + + self->m_provider_ptr = prov; // keep a weak_ptr for the context itself so that it can give out refs. to it's provider + ctxt->initialize(); + return prov; } }}; diff --git a/src/remoteClient/clientContextImpl.h b/src/remoteClient/clientContextImpl.h index 7843695..36c9a38 100644 --- a/src/remoteClient/clientContextImpl.h +++ b/src/remoteClient/clientContextImpl.h @@ -77,13 +77,13 @@ namespace epics { /** * Initialize client context. This method is called immediately after instance construction (call of constructor). */ - virtual void initialize() = 0; + virtual void initialize() = 0; // public? /** * Get channel provider implementation. * @return the channel provider. */ - virtual ChannelProvider::shared_pointer const & getProvider() = 0; + //virtual ChannelProvider::shared_pointer const & getProvider() = 0; /** * Prints detailed information about the context to the standard output stream. @@ -132,7 +132,7 @@ namespace epics { virtual const osiSockAddr& getLocalBroadcastAddress() const = 0; }; - epicsShareExtern ClientContextImpl::shared_pointer createClientContextImpl(); + epicsShareExtern ChannelProvider::shared_pointer createClientProvider(const Configuration::shared_pointer& conf); } } diff --git a/testApp/remote/testRemoteClientImpl.cpp b/testApp/remote/testRemoteClientImpl.cpp index dcb54e5..73ec41a 100644 --- a/testApp/remote/testRemoteClientImpl.cpp +++ b/testApp/remote/testRemoteClientImpl.cpp @@ -371,17 +371,6 @@ int main() { for (int i = 0; i < 10; i++) { { - /* - ClientContextImpl::shared_pointer context = createClientContextImpl(); - context->printInfo(); - - context->initialize(); - context->printInfo(); - - epicsThreadSleep ( SLEEP_TIME ); - - ChannelProvider::shared_pointer provider = context->getProvider(); - */ ClientFactory::start(); ChannelProvider::shared_pointer provider = getChannelProviderRegistry()->getProvider("pva"); From 52069668753e4526e0d3dd83bd54df94702e66f2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 4 Sep 2015 14:55:41 -0400 Subject: [PATCH 16/51] BlockingTCPAcceptor allow bind to specific interface --- src/remote/blockingTCP.h | 5 ++++- src/remote/blockingTCPAcceptor.cpp | 33 ++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/remote/blockingTCP.h b/src/remote/blockingTCP.h index 1a314da..6c03793 100644 --- a/src/remote/blockingTCP.h +++ b/src/remote/blockingTCP.h @@ -125,6 +125,9 @@ namespace epics { BlockingTCPAcceptor(Context::shared_pointer const & context, ResponseHandlerFactory::shared_pointer const & responseHandlerFactory, int port, int receiveBufferSize); + BlockingTCPAcceptor(Context::shared_pointer const & context, + ResponseHandlerFactory::shared_pointer const & responseHandlerFactory, + const osiSockAddr& addr, int receiveBufferSize); virtual ~BlockingTCPAcceptor(); @@ -182,7 +185,7 @@ namespace epics { * Initialize connection acception. * @return port where server is listening */ - int initialize(unsigned short port); + int initialize(); /** * Validate connection by sending a validation message request. diff --git a/src/remote/blockingTCPAcceptor.cpp b/src/remote/blockingTCPAcceptor.cpp index ae75275..9a14360 100644 --- a/src/remote/blockingTCPAcceptor.cpp +++ b/src/remote/blockingTCPAcceptor.cpp @@ -38,25 +38,42 @@ namespace pvAccess { epicsThreadStackMedium), epicsThreadPriorityMedium) { - initialize(port); + _bindAddress.ia.sin_family = AF_INET; + _bindAddress.ia.sin_port = htons(port); + _bindAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + initialize(); + } + + BlockingTCPAcceptor::BlockingTCPAcceptor(Context::shared_pointer const & context, + ResponseHandlerFactory::shared_pointer const & responseHandlerFactory, + const osiSockAddr& addr, int receiveBufferSize) : + _context(context), + _responseHandlerFactory(responseHandlerFactory), + _bindAddress(), + _serverSocketChannel(INVALID_SOCKET), + _receiveBufferSize(receiveBufferSize), + _destroyed(false), + _thread(*this, "TCP-acceptor", + epicsThreadGetStackSize( + epicsThreadStackMedium), + epicsThreadPriorityMedium) + { + _bindAddress = addr; + initialize(); } BlockingTCPAcceptor::~BlockingTCPAcceptor() { destroy(); } - int BlockingTCPAcceptor::initialize(unsigned short port) { - // specified bind address - _bindAddress.ia.sin_family = AF_INET; - _bindAddress.ia.sin_port = htons(port); - _bindAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + int BlockingTCPAcceptor::initialize() { - char strBuffer[64]; char ipAddrStr[48]; ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr)); int tryCount = 0; while(tryCount<2) { + char strBuffer[64]; LOG(logLevelDebug, "Creating acceptor to %s.", ipAddrStr); @@ -83,7 +100,7 @@ namespace pvAccess { LOG( logLevelDebug, "Configured TCP port %d is unavailable, trying to assign it dynamically.", - port); + ntohs(_bindAddress.ia.sin_port)); _bindAddress.ia.sin_port = htons(0); } else { From 5d744dbebe473b150f7ff95f92a2a7025230d74e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 9 Dec 2015 18:06:01 -0500 Subject: [PATCH 17/51] BlockingUDPTransport: add replyTransport allow replies to be sent out on a different socket. Needed when binding the an interface broadcast address. Also, expose the ctor and deprecate pointless create() method. --- src/remote/blockingUDP.h | 21 +++++++++++++++++---- src/remote/blockingUDPConnector.cpp | 4 ++-- src/remote/blockingUDPTransport.cpp | 13 +++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/remote/blockingUDP.h b/src/remote/blockingUDP.h index 554204b..682ccec 100644 --- a/src/remote/blockingUDP.h +++ b/src/remote/blockingUDP.h @@ -46,19 +46,20 @@ namespace epics { public: POINTER_DEFINITIONS(BlockingUDPTransport); - private: BlockingUDPTransport(bool serverFlag, std::auto_ptr &responseHandler, SOCKET channel, osiSockAddr &bindAddress, short remoteTransportRevision); - public: + static shared_pointer create(bool serverFlag, std::auto_ptr& responseHandler, SOCKET channel, osiSockAddr& bindAddress, - short remoteTransportRevision) + short remoteTransportRevision) EPICS_DEPRECATED { shared_pointer thisPointer( - new BlockingUDPTransport(serverFlag, responseHandler, channel, bindAddress, remoteTransportRevision) + new BlockingUDPTransport(serverFlag, responseHandler, + channel, bindAddress, + remoteTransportRevision) ); return thisPointer; } @@ -69,6 +70,11 @@ namespace epics { return _closed.get(); } + void setReplyTransport(const Transport::shared_pointer& T) + { + _replyTransport = T; + } + virtual const osiSockAddr* getRemoteAddress() const { return &_remoteAddress; } @@ -324,6 +330,13 @@ namespace epics { */ SOCKET _channel; + /** When provided, this transport is used for replies (passed to handler) + * instead of *this. This feature is used in the situation where broadcast + * traffic is received on one socket, but a different socket must be used + * for unicast replies. + */ + Transport::shared_pointer _replyTransport; + /** * Bind address. */ diff --git a/src/remote/blockingUDPConnector.cpp b/src/remote/blockingUDPConnector.cpp index 10dc29f..7057f68 100644 --- a/src/remote/blockingUDPConnector.cpp +++ b/src/remote/blockingUDPConnector.cpp @@ -68,8 +68,8 @@ namespace epics { } // sockets are blocking by default - Transport::shared_pointer transport = BlockingUDPTransport::create(_serverFlag, - responseHandler, socket, bindAddress, transportRevision); + Transport::shared_pointer transport(new BlockingUDPTransport(_serverFlag, responseHandler, + socket, bindAddress, transportRevision)); return transport; } diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index 36dabdc..d8621b1 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -40,8 +40,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so PVACCESS_REFCOUNT_MONITOR_DEFINE(blockingUDPTransport); - BlockingUDPTransport::BlockingUDPTransport( - bool serverFlag, + BlockingUDPTransport::BlockingUDPTransport(bool serverFlag, auto_ptr& responseHandler, SOCKET channel, osiSockAddr& bindAddress, short /*remoteTransportRevision*/) : @@ -213,7 +212,8 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so osiSockAddr fromAddress; osiSocklen_t addrStructSize = sizeof(sockaddr); - Transport::shared_pointer thisTransport = shared_from_this(); + Transport::shared_pointer thisTransport = shared_from_this(), + replyTo(_replyTransport ? _replyTransport : thisTransport); try { @@ -249,7 +249,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so _receiveBuffer->flip(); try{ - processBuffer(thisTransport, fromAddress, _receiveBuffer.get()); + processBuffer(replyTo, fromAddress, _receiveBuffer.get()); }catch(std::exception& e){ LOG(logLevelError, "an exception caught while in UDP receiveThread at %s:%d: %s", @@ -304,7 +304,8 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so _shutdownEvent.signal(); } - bool BlockingUDPTransport::processBuffer(Transport::shared_pointer const & thisTransport, osiSockAddr& fromAddress, ByteBuffer* receiveBuffer) { + bool BlockingUDPTransport::processBuffer(Transport::shared_pointer const & replyTransport, + osiSockAddr& fromAddress, ByteBuffer* receiveBuffer) { // handle response(s) while(likely((int)receiveBuffer->getRemaining()>=PVA_MESSAGE_HEADER_SIZE)) { @@ -342,7 +343,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so if(unlikely(nextRequestPosition>receiveBuffer->getLimit())) return false; // handle - _responseHandler->handleResponse(&fromAddress, thisTransport, + _responseHandler->handleResponse(&fromAddress, replyTransport, version, command, payloadSize, _receiveBuffer.get()); From b3d58266a76bfb531b3bbc689eb587ed55aeb870 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Dec 2015 15:24:06 -0500 Subject: [PATCH 18/51] ServerContextImpl: bind to a single interface Allow config option EPICS_PVAS_INTF_ADDR_LIST specify a single interface (multiple interfaces to be handled later) Bind TCP listen and UDP sender to the interface address. For non-windows, bind a second UDP socket to the interface broadcast address. Allow dynamic broadcast port --- src/remote/blockingUDPTransport.cpp | 2 +- src/server/serverContext.cpp | 70 +++++++++++++++++++++++++---- src/server/serverContext.h | 4 +- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index d8621b1..7caa4a4 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -434,7 +434,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so imreq.imr_multiaddr.s_addr = mcastAddr.ia.sin_addr.s_addr; imreq.imr_interface.s_addr = nifAddr.ia.sin_addr.s_addr; - // join multicast group on default interface + // join multicast group on the given interface int status = ::setsockopt(_channel, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&imreq, sizeof(struct ip_mreq)); if (status) diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index a747670..6ec64dc 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -138,6 +138,11 @@ void ServerContextImpl::loadConfiguration() if (debugLevel > 0) SET_LOG_LEVEL(logLevelDebug); + _ifaceAddr.ia.sin_family = AF_INET; + _ifaceAddr.ia.sin_addr.s_addr = htonl(INADDR_ANY); + _ifaceAddr.ia.sin_port = 0; + config->getPropertyAsAddress("EPICS_PVAS_INTF_ADDR_LIST", &_ifaceAddr); + _beaconAddressList = config->getPropertyAsString("EPICS_PVA_ADDR_LIST", _beaconAddressList); _beaconAddressList = config->getPropertyAsString("EPICS_PVAS_BEACON_ADDR_LIST", _beaconAddressList); @@ -149,6 +154,7 @@ void ServerContextImpl::loadConfiguration() _serverPort = config->getPropertyAsInteger("EPICS_PVA_SERVER_PORT", _serverPort); _serverPort = config->getPropertyAsInteger("EPICS_PVAS_SERVER_PORT", _serverPort); + _ifaceAddr.ia.sin_port = htons(_serverPort); _broadcastPort = config->getPropertyAsInteger("EPICS_PVA_BROADCAST_PORT", _broadcastPort); _broadcastPort = config->getPropertyAsInteger("EPICS_PVAS_BROADCAST_PORT", _broadcastPort); @@ -158,6 +164,21 @@ void ServerContextImpl::loadConfiguration() _channelProviderNames = config->getPropertyAsString("EPICS_PVA_PROVIDER_NAMES", _channelProviderNames); _channelProviderNames = config->getPropertyAsString("EPICS_PVAS_PROVIDER_NAMES", _channelProviderNames); + + _ifaceBCast.ia.sin_family = AF_UNSPEC; + if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { + ELLLIST alist = ELLLIST_INIT; + SOCKET sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); + if(sock) { + osiSockDiscoverBroadcastAddresses(&alist, sock, &_ifaceAddr); + epicsSocketDestroy(sock); + } + if(ellCount(&alist)>0) { + osiSockAddrNode *node = (osiSockAddrNode*)ellFirst(&alist); + _ifaceBCast = node->addr; + } + ellFree(&alist); + } } bool ServerContextImpl::isChannelProviderNamePreconfigured() @@ -246,7 +267,7 @@ void ServerContextImpl::internalInitialize() ServerContextImpl::shared_pointer thisServerContext = shared_from_this(); - _acceptor.reset(new BlockingTCPAcceptor(thisServerContext, thisServerContext, _serverPort, _receiveBufferSize)); + _acceptor.reset(new BlockingTCPAcceptor(thisServerContext, thisServerContext, _ifaceAddr, _receiveBufferSize)); _serverPort = ntohs(_acceptor->getBindAddress()->ia.sin_port); // setup broadcast UDP transport @@ -265,7 +286,7 @@ void ServerContextImpl::initializeBroadcastTransport() osiSockAddr listenLocalAddress; listenLocalAddress.ia.sin_family = AF_INET; listenLocalAddress.ia.sin_port = htons(_broadcastPort); - listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + listenLocalAddress.ia.sin_addr.s_addr = _ifaceAddr.ia.sin_addr.s_addr; // where to send addresses SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -284,7 +305,36 @@ void ServerContextImpl::initializeBroadcastTransport() nullTransportClient, responseHandler, listenLocalAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); + listenLocalAddress = *_broadcastTransport->getRemoteAddress(); _broadcastTransport->setSendAddresses(broadcastAddresses.get()); + _broadcastPort = ntohs(listenLocalAddress.ia.sin_port); + +#if !defined(_WIN32) + if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { + if(_ifaceBCast.ia.sin_family == AF_UNSPEC || + _ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { + LOG(logLevelWarn, "Unable to find broadcast address of interface\n"); + } else { + /* An oddness of BSD sockets (not winsock) is that binding to + * INADDR_ANY will receive unicast and broadcast, but binding to + * a specific interface address receives only unicast. The trick + * is to bind a second socket to the interface broadcast address, + * which will then receive only broadcasts. + */ + _ifaceBCast.ia.sin_port = listenLocalAddress.ia.sin_port; + + responseHandler = createResponseHandler(); + _broadcastTransport2 = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, responseHandler, + _ifaceBCast, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + /* The other wrinkle is that nothing should be sent from this second + * socket. So replies are made through the unicast socket. + */ + _broadcastTransport2->setReplyTransport(_broadcastTransport); + } + } +#endif // set ignore address list if (!_ignoreAddressList.empty()) @@ -324,7 +374,7 @@ void ServerContextImpl::initializeBroadcastTransport() { osiSockAddr group; aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); - _broadcastTransport->join(group, loAddr); + _broadcastTransport->join(group, _ifaceAddr); osiSockAddr anyAddress; anyAddress.ia.sin_family = AF_INET; @@ -356,6 +406,8 @@ void ServerContextImpl::initializeBroadcastTransport() } _broadcastTransport->start(); + if(_broadcastTransport2) + _broadcastTransport2->start(); if (_localMulticastTransport) _localMulticastTransport->start(); } @@ -455,11 +507,13 @@ void ServerContextImpl::destroy() void ServerContextImpl::internalDestroy() { // stop responding to search requests - if (_broadcastTransport.get()) - { - _broadcastTransport->close(); - _broadcastTransport.reset(); - } + if (_broadcastTransport) + _broadcastTransport->close(); + if (_broadcastTransport2) + _broadcastTransport2->close(); + _broadcastTransport.reset(); + _broadcastTransport2.reset(); + // and close local multicast transport if (_localMulticastTransport.get()) { diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 80a3987..3d22720 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -312,6 +312,8 @@ private: */ std::string _beaconAddressList; + osiSockAddr _ifaceAddr, _ifaceBCast; + /** * A space-separated list of address from which to ignore name resolution requests. * Each address must be of the form: ip.number:port or host.name:port @@ -351,7 +353,7 @@ private: /** * Broadcast transport needed for channel searches. */ - BlockingUDPTransport::shared_pointer _broadcastTransport; + BlockingUDPTransport::shared_pointer _broadcastTransport, _broadcastTransport2; /** * Local broadcast transport needed for local fan-out. From 609237a551c88fb5d21dcadf31d81d79b6c2448f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Dec 2015 17:31:58 -0500 Subject: [PATCH 19/51] TODO: add virtual dtor for all classes w/ virtual methods --- src/client/pvAccess.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/pvAccess.h b/src/client/pvAccess.h index f7f275c..2fa33d3 100644 --- a/src/client/pvAccess.h +++ b/src/client/pvAccess.h @@ -614,6 +614,8 @@ namespace pvAccess { public: POINTER_DEFINITIONS(Channel); + virtual ~Channel() {} + /** * Channel connection status. */ From 7b726698600f8328665ae2b18202e580ce324198 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Dec 2015 17:32:26 -0500 Subject: [PATCH 20/51] todo: locking in channelmonitor --- src/server/responseHandlers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index d1db8c0..8823876 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -1987,7 +1987,7 @@ void ServerMonitorRequesterImpl::monitorConnect(const Status& status, Monitor::s { Lock guard(_mutex); _status = status; - _channelMonitor = monitor; + _channelMonitor = monitor; //TODO inconsistent locking for _channelMonitor _structure = structure; } TransportSender::shared_pointer thisSender = shared_from_this(); From b64e99c6808c1c62a7812d321ec6f9a525912871 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Dec 2015 10:40:31 -0500 Subject: [PATCH 21/51] todo: attempt to fix testChannelAccess --- testApp/remote/Makefile | 2 +- testApp/remote/channelAccessIFTest.cpp | 69 ++++++++++++++-- testApp/remote/testChannelAccess.cpp | 104 ------------------------- testApp/remote/testServer.cpp | 100 +++++++++++++++++------- 4 files changed, 136 insertions(+), 139 deletions(-) delete mode 100755 testApp/remote/testChannelAccess.cpp diff --git a/testApp/remote/Makefile b/testApp/remote/Makefile index a161fab..ceea70e 100644 --- a/testApp/remote/Makefile +++ b/testApp/remote/Makefile @@ -3,7 +3,7 @@ SRC_DIRS += $(PVACCESS_TEST)/remote TESTPROD_HOST += testChannelAccess -testChannelAccess_SRCS = testChannelAccess channelAccessIFTest +testChannelAccess_SRCS = channelAccessIFTest.cpp testHarness_SRCS += testChannelAccess.cpp channelAccessIFTest.cpp TESTS += testChannelAccess diff --git a/testApp/remote/channelAccessIFTest.cpp b/testApp/remote/channelAccessIFTest.cpp index e137277..e31c037 100755 --- a/testApp/remote/channelAccessIFTest.cpp +++ b/testApp/remote/channelAccessIFTest.cpp @@ -21,6 +21,9 @@ #include "channelAccessIFTest.h" //#define ENABLE_STRESS_TESTS +#define TESTSERVERNOMAIN + +#include "testServer.cpp" using namespace std::tr1; @@ -38,14 +41,43 @@ std::string ChannelAccessIFTest::TEST_SUMRPC_CHANNEL_NAME = "testSum"; // double[] value std::string ChannelAccessIFTest::TEST_ARRAY_CHANNEL_NAME = "testArray1"; +#ifdef ENABLE_STRESS_TESTS +#define EXTRA_STRESS_TESTS 5 +#else +#define EXTRA_STRESS_TESTS 0 +#endif + +namespace { +struct ScopedClientFactory { + ScopedClientFactory() { ClientFactory::start(); } + ~ScopedClientFactory() { ClientFactory::stop(); } +}; +} int ChannelAccessIFTest::runAllTest() { -#ifdef ENABLE_STRESS_TESTS - testPlan(158); -#else - testPlan(153); -#endif + testPlan(153+EXTRA_STRESS_TESTS); + + Configuration::shared_pointer base_config(ConfigurationBuilder() + .add("EPICS_PVAS_INTF_ADDR_LIST", "127.0.0.1") + .add("EPICS_PVA_ADDR_LIST", "127.0.0.1") + .add("EPICS_PVA_AUTO_ADDR_LIST","0") + .add("EPICS_PVA_SERVER_PORT", "0") + .add("EPICS_PVA_BROADCAST_PORT", "0") + .push_map() + .build()); + + TestServer::shared_pointer tstserv(new TestServer(base_config)); + testDiag("TestServer on ports TCP=%u UDP=%u\n", + tstserv->getServerPort(), + tstserv->getBroadcastPort()); + ConfigurationFactory::registerConfiguration("pvAccess-client", + ConfigurationBuilder() + .push_config(base_config) + .add("EPICS_PVA_BROADCAST_PORT", tstserv->getBroadcastPort()) + .push_map() + .build()); + ScopedClientFactory SCF; test_implementation(); test_providerName(); @@ -2348,3 +2380,30 @@ PVStructure::shared_pointer ChannelAccessIFTest::createArrayPvRequest() { pvFieldName->put("value"); return pvRequest; } + + +class ChannelAccessIFRemoteTest: public ChannelAccessIFTest { + + public: + + virtual ChannelProvider::shared_pointer getChannelProvider() { + return getChannelProviderRegistry()->getProvider( + "pva"); + } + + + virtual long getTimeoutSec() { + return 3; + } + + + virtual bool isLocal() { return false;} + +}; + +MAIN(testChannelAccess) +{ + SET_LOG_LEVEL(logLevelError); + ChannelAccessIFRemoteTest caRemoteTest; + return caRemoteTest.runAllTest(); +} diff --git a/testApp/remote/testChannelAccess.cpp b/testApp/remote/testChannelAccess.cpp deleted file mode 100755 index 3e343ba..0000000 --- a/testApp/remote/testChannelAccess.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * testChannelAccess.cpp - */ - -#ifdef _WIN32 -#define NOMINMAX -#endif - -// TODO not nice -// disable buggy boost enable_shared_from_this assert code -#define BOOST_DISABLE_ASSERTS - -#define TESTSERVERNOMAIN - -#include -#include -#include -#include - -#include -#include - -#include "channelAccessIFTest.h" - -#include "testServer.cpp" - - -class ServerContextAction : public Runnable { - - public: - - ServerContextAction(): - m_serverThread(){} - - - virtual void run() - { - testServer(0); - } - - - void stop() { - testServerShutdown(); - } - - - void start() { - m_serverThread.reset(new epics::pvData::Thread("pvAccess", highPriority, this)); - } - - - private: - auto_ptr m_serverThread; -}; - - -class ChannelAccessIFRemoteTest: public ChannelAccessIFTest { - - public: - - ChannelAccessIFRemoteTest(): m_serverContextAction() - { - m_serverContextAction.start(); - ClientFactory::start(); - } - - - virtual ChannelProvider::shared_pointer getChannelProvider() { - return getChannelProviderRegistry()->getProvider( - "pva"); - } - - - virtual long getTimeoutSec() { - return 3; - } - - - virtual bool isLocal() { return false;} - - - ~ChannelAccessIFRemoteTest() { - m_serverContextAction.stop(); - ClientFactory::stop(); - - // shutdown SIGSEG problems - epicsThreadSleep(2.0); - } - - private: - ServerContextAction m_serverContextAction; -}; - - -MAIN(testChannelAccess) -{ - // note: this leaks memory (uses putenv) - epicsEnvSet("EPICS_PVA_ADDR_LIST", "127.0.0.1"); - epicsEnvSet("EPICS_PVA_AUTO_ADDR_LIST", "0"); - - SET_LOG_LEVEL(logLevelError); - ChannelAccessIFRemoteTest caRemoteTest; - return caRemoteTest.runAllTest(); -} diff --git a/testApp/remote/testServer.cpp b/testApp/remote/testServer.cpp index cacbb26..b49fdc2 100644 --- a/testApp/remote/testServer.cpp +++ b/testApp/remote/testServer.cpp @@ -1554,12 +1554,14 @@ public: m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), result); } +#ifndef TESTSERVERNOMAIN else if (channelName.find("testServerShutdown") == 0) { PVStructure::shared_pointer nullPtr; m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), nullPtr); testServerShutdown(); } +#endif else { /* @@ -2727,44 +2729,76 @@ public: }; - -static ServerContextImpl::shared_pointer ctx; - -void testServer(int timeToRun) +struct TestServer : public Runnable { + POINTER_DEFINITIONS(TestServer); - MockChannelProviderFactory::shared_pointer factory(new MockChannelProviderFactory()); - registerChannelProviderFactory(factory); + static TestServer::shared_pointer ctx; - //ServerContextImpl::shared_pointer ctx = ServerContextImpl::create(); - ctx = ServerContextImpl::create(); - ctx->initialize(getChannelProviderRegistry()); + Configuration::shared_pointer conf; + ServerContextImpl::shared_pointer context; + Event startup; + Thread runner; + MockChannelProviderFactory::shared_pointer factory; - ctx->printInfo(); - - ctx->run(timeToRun); - - ctx->destroy(); - - unregisterChannelProviderFactory(factory); - - structureChangedListeners.clear(); + TestServer(const Configuration::shared_pointer& conf) + :conf(conf) + ,runner(Thread::Config(this).name("TestServer").autostart(false)) + ,factory(new MockChannelProviderFactory()) { - Lock guard(structureStoreMutex); - structureStore.clear(); + registerChannelProviderFactory(factory); + + context = ServerContextImpl::create(conf); + context->initialize(getChannelProviderRegistry()); + + runner.start(); + startup.wait(); // wait for thread to start } - ctx.reset(); + ~TestServer() + { + context->shutdown(); + runner.exitWait(); + context->destroy(); - unregisterChannelProviderFactory(factory); + unregisterChannelProviderFactory(factory); + + structureChangedListeners.clear(); + { + Lock guard(structureStoreMutex); + structureStore.clear(); + } + ctx.reset(); + + unregisterChannelProviderFactory(factory); - shutdownSimADCs(); -} + shutdownSimADCs(); + } + // Use with EPICS_PVA_SERVER_PORT==0 for dynamic port (unit-tests) + unsigned short getServerPort() + { + return context->getServerPort(); + } + unsigned short getBroadcastPort() + { + return context->getBroadcastPort(); + } + virtual void run() + { + startup.signal(); + context->run(conf->getPropertyAsInteger("timeToRun", 0)); // default is no timeout + } + void shutdown() { + context->shutdown(); + } +}; + +TestServer::shared_pointer TestServer::ctx; + void testServerShutdown() { - // NOTE: this is not thread-safe TODO - ctx->shutdown(); + TestServer::ctx->shutdown(); } #include @@ -2789,7 +2823,7 @@ int main(int argc, char *argv[]) int opt; /* getopt() current option */ bool debug = false; bool cleanupAndReport = false; - int timeToRun = 0; + std::string timeToRun("0"); setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ @@ -2799,7 +2833,7 @@ int main(int argc, char *argv[]) usage(argv); return 0; case 't': /* Print usage */ - timeToRun = atoi(optarg); + timeToRun = optarg; break; case 'd': /* Debug log level */ debug = true; @@ -2827,7 +2861,15 @@ int main(int argc, char *argv[]) srand ( time(NULL) ); - testServer(timeToRun); + { + TestServer::shared_pointer srv(new TestServer(ConfigurationBuilder() + .push_env() + .add("timeToRun", timeToRun) + .push_map() + .build())); + TestServer::ctx = srv; + } + TestServer::ctx.reset(); cout << "Done" << endl; From 96deab1e778624d0ffc0585c1eeab1811f67e01d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 8 Sep 2015 16:53:55 -0400 Subject: [PATCH 22/51] todo: placeholder needs to be filled in --- src/server/responseHandlers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 8823876..1f04c1d 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -844,6 +844,8 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer const & /*channel*/, const Channel::ConnectionState /*isConnected*/) { // TODO should we notify remote side? + + // YES :) } string ServerChannelRequesterImpl::getRequesterName() From b3500464277cc02c202a798ac2ff166cfc7d63a4 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 14 Dec 2015 14:14:45 -0500 Subject: [PATCH 23/51] todo: udprepeat --- src/utils/udprepeat.cpp | 172 ++++++++++++++++++++++++++++++++++++++++ src/utils/udprepeat.h | 54 +++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/utils/udprepeat.cpp create mode 100644 src/utils/udprepeat.h diff --git a/src/utils/udprepeat.cpp b/src/utils/udprepeat.cpp new file mode 100644 index 0000000..5c4f3d8 --- /dev/null +++ b/src/utils/udprepeat.cpp @@ -0,0 +1,172 @@ + +#include +#include + +#include +#include + +#include + +#include "udprepeat.h" + +namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; + +typedef epicsGuard Guard; +typedef epicsGuardRelease UnGuard; + +namespace { +static epicsMutex repeatlistlock; +typedef std::map repeaters_t; +static repeaters_t repeaters; + +struct Receiver : public pvd::Runnable +{ + const SOCKET sock; + pva::UDPFanout::Pvt * const pvt; + std::vector buf; + std::vector > recvs; + bool run; + pvd::Thread runner; + epicsEvent evt; + + Receiver(SOCKET sock, const std::string& name, pva::UDPFanout::Pvt *pvt) + :sock(sock), pvt(pvt), buf(8196), run(true) + ,runner(pvd::Thread::Config(this) + .name(name) + .prio(epicsThreadPriorityCAServerLow-2)) + { + evt.wait(); + } + ~Receiver() + { + epicsSocketDestroy(sock); + } + virtual void run() + { + evt.signal(); + Guard G(pvt->lock); + while(run) { + ssize_t ret; + { + UnGuard U(G); + ret = ::recv(sock, &buf[0], buf.size(), 0); + } + if(ret<0) { + int err = errno; + + } + } + } + void shutdown(); +}; + +} + +namespace epics {namespace pvAccess { + +struct UDPFanout::Pvt +{ + const unsigned port; + epicsMutex lock; + + UDPFanout::addr_list iface, bcase; + UDPFanout::name_list names; + + Pvt(unsigned port) + :port(port) + { + + } +}; + +}} // namespace + +namespace { + +void Receiver::shutdown() +{ + { + Guard G(pvt->lock); + run = false; + } + switch(epicsSocketSystemCallInterruptMechanismQuery()) + { + case esscimqi_socketBothShutdownRequired: + ::shutdown(sock, SHUT_RDWR); + break; + default: + break; + } + runner.exitWait(); +} + +} + +namespace epics {namespace pvAccess { + + +UDPFanout::~UDPFanout() +{ + std::auto_ptr pvt(this->pvt); + + epicsGuard G(repeatlistlock); + + repeaters_t::iterator it = repeaters.find(pvt->port); + if(it!=repeaters.end()) { + UDPFanout::shared_pointer self(it->second.lock()); + if(self && self.get()==this) + repeaters.erase(it); + } +} + +void +UDPFanout::bind(const UDPReceiver::shared_pointer, const std::string& iname) +{ + +} + +void +UDPFanout::bind(const UDPReceiver::shared_pointer, const osiSockAddr& iface) +{ + +} + +void +UDPFanout::unbind(const UDPReceiver::shared_pointer) +{ + +} + +const UDPFanout::name_list& +UDPFanout::getNames() +{ + +} + +const UDPFanout::addr_list& +UDPFanout::getAddresses() +{ + +} + +UDPFanout::shared_pointer +UDPFanout::getFanoutPort(unsigned port) +{ + epicsGuard G(repeatlistlock); + + repeaters_t::const_iterator it = repeaters.find(port); + if(it!=repeaters.end()) { + UDPFanout::shared_pointer R(it->second.lock()); + if(R) + return R; + } + + std::auto_ptr pvt(new UDPFanout::Pvt(port)); + UDPFanout::shared_pointer ret(new UDPFanout(pvt.get())); + repeaters[port] = ret; + pvt.release(); + return ret; +} + +}} // namespace diff --git a/src/utils/udprepeat.h b/src/utils/udprepeat.h new file mode 100644 index 0000000..eb9aabe --- /dev/null +++ b/src/utils/udprepeat.h @@ -0,0 +1,54 @@ +#ifndef UDPREPEAT_H +#define UDPREPEAT_H + +#include +#include + +#include + +#include "byteBuffer.h" +#include "sharedPtr.h" + +#define epicsExportSharedSymbols + +namespace epics {namespace pvAccess { + +class UDPReceiver +{ + POINTER_DEFINITIONS(UDPReceiver); + + virtual ~UDPReceiver() {} + + virtual void recv(const osiSockAddr& src, + const char *buf, size_t buflen) =0; +}; + +class UDPFanout +{ + struct Pvt; + friend struct Pvt; + Pvt *pvt; + UDPFanout(Pvt *pvt) :pvt(pvt) {} + UDPFanout(const UDPFanout&); + UDPFanout& operator=(const UDPFanout&); +public: + POINTER_DEFINITIONS(UDPFanout); + + ~UDPFanout(); + + void bind(const UDPReceiver::shared_pointer, const std::string& iname); + void bind(const UDPReceiver::shared_pointer, const osiSockAddr& iface); + void unbind(const UDPReceiver::shared_pointer); + + typedef std::vector name_list; + typedef std::vector addr_list; + + const name_list& getNames(); + const addr_list& getAddresses(); + + static UDPFanout::shared_pointer getFanoutPort(unsigned port); +}; + +}} + +#endif // UDPREPEAT_H From 76b414dac631ad53ab4961a4b61319acfc963e47 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 14 Dec 2015 14:15:03 -0500 Subject: [PATCH 24/51] todo: disable local mcast fanout --- src/server/responseHandlers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 1f04c1d..8cf4a22 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -263,7 +263,7 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, // // locally broadcast if unicast (qosCode & 0x80 == 0x80) // - if ((qosCode & 0x80) == 0x80) + if (0) { BlockingUDPTransport::shared_pointer bt = _context->getLocalMulticastTransport(); if (bt) From d7eafcb9c53793ee232a74f8025783fb5287d93e Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 16 Dec 2015 11:46:58 +0100 Subject: [PATCH 25/51] EPICS base 3.14/clang compilation, TCPAcceptor shutdown on OSX (BSD) fixed --- src/remote/blockingTCPAcceptor.cpp | 2 +- src/remote/blockingUDPConnector.cpp | 5 +- src/remoteClient/clientContextImpl.cpp | 4 +- src/utils/configuration.cpp | 124 +++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 4 deletions(-) diff --git a/src/remote/blockingTCPAcceptor.cpp b/src/remote/blockingTCPAcceptor.cpp index 9a14360..565e65c 100644 --- a/src/remote/blockingTCPAcceptor.cpp +++ b/src/remote/blockingTCPAcceptor.cpp @@ -268,8 +268,8 @@ namespace pvAccess { { case esscimqi_socketBothShutdownRequired: shutdown(sock, SHUT_RDWR); - _thread.exitWait(); epicsSocketDestroy(sock); + _thread.exitWait(); break; case esscimqi_socketSigAlarmRequired: LOG(logLevelError, "SigAlarm close not implemented for this target\n"); diff --git a/src/remote/blockingUDPConnector.cpp b/src/remote/blockingUDPConnector.cpp index 7057f68..676ce47 100644 --- a/src/remote/blockingUDPConnector.cpp +++ b/src/remote/blockingUDPConnector.cpp @@ -68,9 +68,10 @@ namespace epics { } // sockets are blocking by default - Transport::shared_pointer transport(new BlockingUDPTransport(_serverFlag, responseHandler, + BlockingUDPTransport::shared_pointer transport(new BlockingUDPTransport(_serverFlag, responseHandler, socket, bindAddress, transportRevision)); - return transport; + + return Transport::shared_pointer(transport); } } diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index a64dfdc..74f625e 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -5060,7 +5060,9 @@ namespace epics { * So we compose the provider within the context and use a nested shared_ptr * to the context for the provider. */ - ClientContextImpl::shared_pointer ctxt(new InternalClientContextImpl(conf)); + // TODO use make::shared + InternalClientContextImpl::shared_pointer t(new InternalClientContextImpl(conf)); + ClientContextImpl::shared_pointer ctxt(t); InternalClientContextImpl *self = static_cast(ctxt.get()); diff --git a/src/utils/configuration.cpp b/src/utils/configuration.cpp index 6b0a356..a49e2db 100644 --- a/src/utils/configuration.cpp +++ b/src/utils/configuration.cpp @@ -4,11 +4,19 @@ * in file LICENSE that is included with this distribution. */ +#include +#include +#include +#include +#include +#include + #include #include #include +#include #include #define epicsExportSharedSymbols @@ -25,6 +33,122 @@ namespace pvAccess { using namespace epics::pvData; using namespace std; +#ifndef EPICS_VERSION_INT +#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) +#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL) +#endif + +#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1) +/* integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */ + +#define S_stdlib_noConversion 1 /* No digits to convert */ +#define S_stdlib_extraneous 2 /* Extraneous characters */ +#define S_stdlib_underflow 3 /* Too small to represent */ +#define S_stdlib_overflow 4 /* Too large to represent */ +#define S_stdlib_badBase 5 /* Number base not supported */ + +static int +epicsParseDouble(const char *str, double *to, char **units) +{ + int c; + char *endp; + double value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = epicsStrtod(str, &endp); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == ERANGE) + return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +static int +epicsParseFloat(const char *str, float *to, char **units) +{ + double value, abs; + int status = epicsParseDouble(str, &value, units); + + if (status) + return status; + + abs = fabs(value); + if (value > 0 && abs <= FLT_MIN) + return S_stdlib_underflow; + // NOTE: 'finite' is deprecated in OS X 10.9, use 'isfinite' instead + if (isfinite(value) && abs >= FLT_MAX) + return S_stdlib_overflow; + + *to = (float)value; + return 0; +} + +static int +epicsParseLong(const char *str, long *to, int base, char **units) +{ + int c; + char *endp; + long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtol(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +static int +epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units) +{ + long value; + int status = epicsParseLong(str, &value, base, units); + + if (status) + return status; + +#if (LONG_MAX > 0x7fffffff) + if (value < -0x80000000L || value > 0x7fffffffL) + return S_stdlib_overflow; +#endif + + *to = (epicsInt32)value; + return 0; +} + +#endif + + + Properties::Properties() {} Properties::Properties(const string &fileName) : _fileName(fileName) {} From 22d6db2818c23bc0bebafe82d838e8dd482a2ffe Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 16 Dec 2015 12:00:20 +0100 Subject: [PATCH 26/51] fixed testServer --- testApp/remote/channelAccessIFTest.cpp | 1 + testApp/remote/testServer.cpp | 35 +++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/testApp/remote/channelAccessIFTest.cpp b/testApp/remote/channelAccessIFTest.cpp index e31c037..05a25ba 100755 --- a/testApp/remote/channelAccessIFTest.cpp +++ b/testApp/remote/channelAccessIFTest.cpp @@ -68,6 +68,7 @@ int ChannelAccessIFTest::runAllTest() { .build()); TestServer::shared_pointer tstserv(new TestServer(base_config)); + tstserv->start(); testDiag("TestServer on ports TCP=%u UDP=%u\n", tstserv->getServerPort(), tstserv->getBroadcastPort()); diff --git a/testApp/remote/testServer.cpp b/testApp/remote/testServer.cpp index b49fdc2..45e1926 100644 --- a/testApp/remote/testServer.cpp +++ b/testApp/remote/testServer.cpp @@ -2750,10 +2750,20 @@ struct TestServer : public Runnable context = ServerContextImpl::create(conf); context->initialize(getChannelProviderRegistry()); - - runner.start(); - startup.wait(); // wait for thread to start } + void start(bool inSameThread = false) + { + if (inSameThread) + { + context->run(conf->getPropertyAsInteger("timeToRun", 0)); // default is no timeout + } + else + { + runner.start(); + startup.wait(); // wait for thread to start + } + } + ~TestServer() { context->shutdown(); @@ -2788,6 +2798,9 @@ struct TestServer : public Runnable startup.signal(); context->run(conf->getPropertyAsInteger("timeToRun", 0)); // default is no timeout } + void waitForShutdown() { + context->shutdown(); + } void shutdown() { context->shutdown(); } @@ -2861,15 +2874,13 @@ int main(int argc, char *argv[]) srand ( time(NULL) ); - { - TestServer::shared_pointer srv(new TestServer(ConfigurationBuilder() - .push_env() - .add("timeToRun", timeToRun) - .push_map() - .build())); - TestServer::ctx = srv; - } - TestServer::ctx.reset(); + TestServer::shared_pointer srv(new TestServer(ConfigurationBuilder() + .push_env() + .add("timeToRun", timeToRun) + .push_map() + .build())); + TestServer::ctx = srv; + srv->start(true); cout << "Done" << endl; From 91b5cc707dc86560d8d8fd6b0ca10f1572feb953 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 16 Dec 2015 13:52:56 +0100 Subject: [PATCH 27/51] fixed testChannelAccess --- src/remoteClient/clientContextImpl.cpp | 10 ++++-- src/utils/inetAddressUtil.cpp | 4 +++ testApp/remote/testServer.cpp | 45 +++++++++++--------------- testApp/utils/testInetAddressUtils.cpp | 3 +- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 74f625e..15e8e31 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -4439,9 +4439,13 @@ namespace epics { } } - for (size_t i = 0; broadcastAddresses.get() && i < broadcastAddresses->size(); i++) - LOG(logLevelDebug, - "Broadcast address #%d: %s.", i, inetAddressToString((*broadcastAddresses)[i]).c_str()); + if (!broadcastAddresses.get() || !broadcastAddresses->size()) + LOG(logLevelWarn, + "No broadcast addresses found or specified!"); + else + for (size_t i = 0; i < broadcastAddresses->size(); i++) + LOG(logLevelDebug, + "Broadcast address #%d: %s.", i, inetAddressToString((*broadcastAddresses)[i]).c_str()); // where to bind (listen) address osiSockAddr listenLocalAddress; diff --git a/src/utils/inetAddressUtil.cpp b/src/utils/inetAddressUtil.cpp index 768a0d6..24301f6 100644 --- a/src/utils/inetAddressUtil.cpp +++ b/src/utils/inetAddressUtil.cpp @@ -26,6 +26,7 @@ namespace pvAccess { void addDefaultBroadcastAddress(InetAddrVector* v, unsigned short p) { osiSockAddr pNewNode; pNewNode.ia.sin_family = AF_INET; + // TODO this does not work in case of no active interfaces, should return 127.0.0.1 then pNewNode.ia.sin_addr.s_addr = htonl(INADDR_BROADCAST); pNewNode.ia.sin_port = htons(p); v->push_back(pNewNode); @@ -47,6 +48,9 @@ InetAddrVector* getBroadcastAddresses(SOCKET sock, v->push_back(sn->addr); } ellFree(&as); + // add fallback address + if (!v->size()) + addDefaultBroadcastAddress(v, defaultPort); return v; } diff --git a/testApp/remote/testServer.cpp b/testApp/remote/testServer.cpp index 45e1926..18b52b0 100644 --- a/testApp/remote/testServer.cpp +++ b/testApp/remote/testServer.cpp @@ -2649,37 +2649,28 @@ public: short /*priority*/, std::string const & address) { - if (address == "local") + // this is a server instance provider, address holds remote socket IP + if (channelName == "testCounter") { - if (channelName == "testCounter") - { - channelRequester->channelCreated(Status::Ok, m_counterChannel); - return m_counterChannel; - } - else if (channelName == "testADC") - { - channelRequester->channelCreated(Status::Ok, m_adcChannel); - return m_adcChannel; - } - else if (channelName == "testMP") - { - channelRequester->channelCreated(Status::Ok, m_mpChannel); - return m_mpChannel; - } - else - { - ChannelProvider::shared_pointer chProviderPtr = shared_from_this(); - Channel::shared_pointer channel = MockChannel::create(chProviderPtr, channelRequester, channelName, address); - channelRequester->channelCreated(Status::Ok, channel); - return channel; - } + channelRequester->channelCreated(Status::Ok, m_counterChannel); + return m_counterChannel; + } + else if (channelName == "testADC") + { + channelRequester->channelCreated(Status::Ok, m_adcChannel); + return m_adcChannel; + } + else if (channelName == "testMP") + { + channelRequester->channelCreated(Status::Ok, m_mpChannel); + return m_mpChannel; } else { - Channel::shared_pointer nullPtr; - Status errorStatus(Status::STATUSTYPE_ERROR, "only local supported"); - channelRequester->channelCreated(errorStatus, nullPtr); - return nullPtr; + ChannelProvider::shared_pointer chProviderPtr = shared_from_this(); + Channel::shared_pointer channel = MockChannel::create(chProviderPtr, channelRequester, channelName, address); + channelRequester->channelCreated(Status::Ok, channel); + return channel; } } private: diff --git a/testApp/utils/testInetAddressUtils.cpp b/testApp/utils/testInetAddressUtils.cpp index 1ffa24d..38cd696 100644 --- a/testApp/utils/testInetAddressUtils.cpp +++ b/testApp/utils/testInetAddressUtils.cpp @@ -180,8 +180,9 @@ void test_getBroadcastAddresses() SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); auto_ptr broadcasts(getBroadcastAddresses(socket, 6678)); - // at least one is expected + // at least one is expected, in case of no network connection a fallback address is returned testOk1(static_cast(0) < broadcasts->size()); + //testDiag("getBroadcastAddresses() returned %zu entry/-ies.", broadcasts->size()); epicsSocketDestroy(socket); // debug From 0b9cdb9633b6c7a7e69121c0196ef80ac84371a9 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 16 Dec 2015 13:54:26 +0100 Subject: [PATCH 28/51] added QtCreator files to .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index a4eb8a2..dc47861 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,7 @@ configure/*.local !configure/ExampleRELEASE.local **/O.* QtC-* +*.config +*.creator +*.files +*.includes From 4a7f057af1bb23d75991993394f873df9ffb9b25 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 16 Dec 2015 14:45:20 +0100 Subject: [PATCH 29/51] server channel destroy notification to the client --- src/remoteClient/clientContextImpl.cpp | 43 ++++++++++++++++++++++++-- src/remoteClient/clientContextImpl.h | 1 + src/server/responseHandlers.cpp | 26 ++++++++++++++-- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 15e8e31..135d371 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -3165,6 +3165,33 @@ namespace epics { }; + class DestroyChannelHandler : public AbstractClientResponseHandler, private epics::pvData::NoDefaultMethods { + public: + DestroyChannelHandler(ClientContextImpl::shared_pointer const & context) : + AbstractClientResponseHandler(context, "Destroy channel") + { + } + + virtual ~DestroyChannelHandler() { + } + + virtual void handleResponse(osiSockAddr* responseFrom, + Transport::shared_pointer const & transport, int8 version, int8 command, + size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer) + { + AbstractClientResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer); + + transport->ensureData(4); + pvAccessID cid = payloadBuffer->getInt(); + /*pvAccessID sid =*/ payloadBuffer->getInt(); + + // TODO optimize + ChannelImpl::shared_pointer channel = static_pointer_cast(_context.lock()->getChannel(cid)); + if (channel.get()) + channel->channelDestroyedOnServer(); + } + }; + /** * PVA response handler - main handler which dispatches responses to appripriate handlers. @@ -3200,7 +3227,7 @@ namespace epics { m_handlerTable[CMD_AUTHNZ].reset(new AuthNZHandler(context.get())); /* 5 */ m_handlerTable[CMD_ACL_CHANGE].reset(new NoopResponse(context, "Access rights change")); /* 6 */ m_handlerTable[CMD_CREATE_CHANNEL].reset(new CreateChannelHandler(context)); /* 7 */ - m_handlerTable[CMD_DESTROY_CHANNEL].reset(new NoopResponse(context, "Destroy channel")); /* 8 */ // TODO it might be useful to implement this... + m_handlerTable[CMD_DESTROY_CHANNEL].reset(new DestroyChannelHandler(context)); /* 8 */ m_handlerTable[CMD_CONNECTION_VALIDATED].reset(new ClientConnectionValidatedHandler(context)); /* 9 */ m_handlerTable[CMD_GET] = dataResponse; /* 10 - get response */ m_handlerTable[CMD_PUT] = dataResponse; /* 11 - put response */ @@ -3859,6 +3886,16 @@ namespace epics { this->initiateSearch(); } + + void channelDestroyedOnServer() { + if (isConnected()) + { + disconnect(true, false); + + // should be called without any lock hold + reportChannelStateChange(); + } + } #define STATIC_SEARCH_BASE_DELAY_SEC 5 #define STATIC_SEARCH_MAX_MULTIPLIER 10 @@ -4059,7 +4096,7 @@ namespace epics { if (issueCreateMessage) { - control->startMessage((int8)7, 2+4); + control->startMessage((int8)CMD_CREATE_CHANNEL, 2+4); // count buffer->putShort((int16)1); @@ -4072,7 +4109,7 @@ namespace epics { } else { - control->startMessage((int8)8, 4+4); + control->startMessage((int8)CMD_DESTROY_CHANNEL, 4+4); // SID m_channelMutex.lock(); pvAccessID sid = m_serverChannelID; diff --git a/src/remoteClient/clientContextImpl.h b/src/remoteClient/clientContextImpl.h index 36c9a38..06e7c02 100644 --- a/src/remoteClient/clientContextImpl.h +++ b/src/remoteClient/clientContextImpl.h @@ -48,6 +48,7 @@ namespace epics { virtual void connectionCompleted(pvAccessID sid/*, rights*/) = 0; virtual void createChannelFailed() = 0; virtual std::tr1::shared_ptr getContext() = 0; + virtual void channelDestroyedOnServer() = 0; virtual pvAccessID getServerChannelID() = 0; virtual void registerResponseRequest(ResponseRequest::shared_pointer const & responseRequest) = 0; diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 8cf4a22..c82f247 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -843,9 +843,31 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer const & /*channel*/, const Channel::ConnectionState /*isConnected*/) { - // TODO should we notify remote side? + if(Transport::shared_pointer transport = _transport.lock()) + { + ChannelHostingTransport::shared_pointer casTransport = dynamic_pointer_cast(transport); + if (!casTransport) + return; - // YES :) + ServerChannelImpl::shared_pointer channel; + { + Lock guard(_mutex); + channel= dynamic_pointer_cast(_serverChannel.lock()); + } + + if (!channel) + return; + + // destroy + channel->destroy(); + + // .. and unregister + casTransport->unregisterChannel(channel->getSID()); + + // send response back + TransportSender::shared_pointer sr(new ServerDestroyChannelHandlerTransportSender(channel->getCID(), channel->getSID())); + transport->enqueueSendRequest(sr); + } } string ServerChannelRequesterImpl::getRequesterName() From a6925461708dcb86143916228f45891eecfcc856 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 16 Dec 2015 23:30:53 +0100 Subject: [PATCH 30/51] ServerChannelRequesterImpl::getRequesterName() returns client's address --- src/client/pvAccess.h | 7 +++-- src/server/responseHandlers.cpp | 12 +++++---- testApp/remote/testServer.cpp | 46 ++++++++++++++++++++------------- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/client/pvAccess.h b/src/client/pvAccess.h index 2fa33d3..ed0f8fd 100644 --- a/src/client/pvAccess.h +++ b/src/client/pvAccess.h @@ -633,8 +633,11 @@ namespace pvAccess { // virtual ChannelProvider::shared_pointer getProvider() = 0; /** - * Returns the channel's remote address, e.g. "/192.168.1.101:5064" or "#C0 S1". - * @return the channel's remote address. + * Returns the channel's remote address, signal name, etc... + * For example: + * - client side channel would return server's address, e.g. "/192.168.1.101:5064" + * - server side channel would return underlying bus address, e.g. "#C0 S1". + * @return the channel's address. **/ virtual std::string getRemoteAddress() = 0; diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index c82f247..7120101 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -760,7 +760,7 @@ ChannelRequester::shared_pointer ServerChannelRequesterImpl::create( std::tr1::shared_ptr tp(new ServerChannelRequesterImpl(transport, channelName, cid, css)); ChannelRequester::shared_pointer cr = tp; // TODO exception guard and report error back - provider->createChannel(channelName, cr, transport->getPriority(), transport->getRemoteName()); + provider->createChannel(channelName, cr, transport->getPriority()); return cr; } @@ -872,9 +872,11 @@ void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer cons string ServerChannelRequesterImpl::getRequesterName() { - std::stringstream name; - name << "ServerChannelRequesterImpl/" << _channelName << "[" << _cid << "]"; - return name.str(); + Transport::shared_pointer transport = _transport.lock(); + if (transport) + return transport->getRemoteName(); + else + return ":0"; } void ServerChannelRequesterImpl::message(std::string const & message, MessageType messageType) @@ -943,7 +945,7 @@ void ServerDestroyChannelHandler::handleResponse(osiSockAddr* responseFrom, ChannelHostingTransport::shared_pointer casTransport = dynamic_pointer_cast(transport); - transport->ensureData(2*sizeof(int32)/sizeof(int8)); + transport->ensureData(8); const pvAccessID sid = payloadBuffer->getInt(); const pvAccessID cid = payloadBuffer->getInt(); diff --git a/testApp/remote/testServer.cpp b/testApp/remote/testServer.cpp index 18b52b0..5ff0e84 100644 --- a/testApp/remote/testServer.cpp +++ b/testApp/remote/testServer.cpp @@ -2649,28 +2649,38 @@ public: short /*priority*/, std::string const & address) { - // this is a server instance provider, address holds remote socket IP - if (channelName == "testCounter") + if (address == "local") { - channelRequester->channelCreated(Status::Ok, m_counterChannel); - return m_counterChannel; - } - else if (channelName == "testADC") - { - channelRequester->channelCreated(Status::Ok, m_adcChannel); - return m_adcChannel; - } - else if (channelName == "testMP") - { - channelRequester->channelCreated(Status::Ok, m_mpChannel); - return m_mpChannel; + // this is a server instance provider, address holds remote socket IP + if (channelName == "testCounter") + { + channelRequester->channelCreated(Status::Ok, m_counterChannel); + return m_counterChannel; + } + else if (channelName == "testADC") + { + channelRequester->channelCreated(Status::Ok, m_adcChannel); + return m_adcChannel; + } + else if (channelName == "testMP") + { + channelRequester->channelCreated(Status::Ok, m_mpChannel); + return m_mpChannel; + } + else + { + ChannelProvider::shared_pointer chProviderPtr = shared_from_this(); + Channel::shared_pointer channel = MockChannel::create(chProviderPtr, channelRequester, channelName, address); + channelRequester->channelCreated(Status::Ok, channel); + return channel; + } } else { - ChannelProvider::shared_pointer chProviderPtr = shared_from_this(); - Channel::shared_pointer channel = MockChannel::create(chProviderPtr, channelRequester, channelName, address); - channelRequester->channelCreated(Status::Ok, channel); - return channel; + Channel::shared_pointer nullPtr; + Status errorStatus(Status::STATUSTYPE_ERROR, "only local supported"); + channelRequester->channelCreated(errorStatus, nullPtr); + return nullPtr; } } private: From 125822d18f9a05d8e183ac409ca6711f633b479f Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 17 Dec 2015 09:12:33 +0100 Subject: [PATCH 31/51] one instance of responseHandler per context --- src/remote/blockingTCP.h | 20 +++++--------------- src/remote/blockingTCPAcceptor.cpp | 11 +++++------ src/remote/blockingTCPConnector.cpp | 2 +- src/remote/blockingUDP.h | 8 ++++---- src/remote/blockingUDPConnector.cpp | 2 +- src/remote/blockingUDPTransport.cpp | 2 +- src/remote/codec.cpp | 4 ++-- src/remote/codec.h | 12 ++++++------ src/remote/remote.h | 2 +- src/remoteClient/clientContextImpl.cpp | 17 ++++++++++------- src/server/serverContext.cpp | 20 ++++++-------------- src/server/serverContext.h | 9 ++++++--- testApp/remote/testServer.cpp | 1 + 13 files changed, 49 insertions(+), 61 deletions(-) diff --git a/src/remote/blockingTCP.h b/src/remote/blockingTCP.h index 6c03793..1237f16 100644 --- a/src/remote/blockingTCP.h +++ b/src/remote/blockingTCP.h @@ -58,7 +58,7 @@ namespace epics { virtual ~BlockingTCPConnector(); virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client, - std::auto_ptr& responseHandler, osiSockAddr& address, + ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& address, epics::pvData::int8 transportRevision, epics::pvData::int16 priority); private: /** @@ -97,16 +97,6 @@ namespace epics { }; - class ResponseHandlerFactory - { - public: - POINTER_DEFINITIONS(ResponseHandlerFactory); - - virtual ~ResponseHandlerFactory() {}; - - virtual std::auto_ptr createResponseHandler() = 0; - }; - /** * Channel Access Server TCP acceptor. * @author Matej Sekoranja @@ -123,10 +113,10 @@ namespace epics { * @throws PVAException */ BlockingTCPAcceptor(Context::shared_pointer const & context, - ResponseHandlerFactory::shared_pointer const & responseHandlerFactory, + ResponseHandler::shared_pointer const & responseHandler, int port, int receiveBufferSize); BlockingTCPAcceptor(Context::shared_pointer const & context, - ResponseHandlerFactory::shared_pointer const & responseHandlerFactory, + ResponseHandler::shared_pointer const & responseHandler, const osiSockAddr& addr, int receiveBufferSize); virtual ~BlockingTCPAcceptor(); @@ -153,9 +143,9 @@ namespace epics { Context::shared_pointer _context; /** - * ResponseHandler factory. + * Response handler. */ - ResponseHandlerFactory::shared_pointer _responseHandlerFactory; + ResponseHandler::shared_pointer _responseHandler; /** * Bind server socket address. diff --git a/src/remote/blockingTCPAcceptor.cpp b/src/remote/blockingTCPAcceptor.cpp index 565e65c..72e4315 100644 --- a/src/remote/blockingTCPAcceptor.cpp +++ b/src/remote/blockingTCPAcceptor.cpp @@ -24,11 +24,11 @@ namespace pvAccess { BlockingTCPAcceptor::BlockingTCPAcceptor( Context::shared_pointer const & context, - ResponseHandlerFactory::shared_pointer const & responseHandlerFactory, + ResponseHandler::shared_pointer const & responseHandler, int port, int receiveBufferSize) : _context(context), - _responseHandlerFactory(responseHandlerFactory), + _responseHandler(responseHandler), _bindAddress(), _serverSocketChannel(INVALID_SOCKET), _receiveBufferSize(receiveBufferSize), @@ -45,10 +45,10 @@ namespace pvAccess { } BlockingTCPAcceptor::BlockingTCPAcceptor(Context::shared_pointer const & context, - ResponseHandlerFactory::shared_pointer const & responseHandlerFactory, + ResponseHandler::shared_pointer const & responseHandler, const osiSockAddr& addr, int receiveBufferSize) : _context(context), - _responseHandlerFactory(responseHandlerFactory), + _responseHandler(responseHandler), _bindAddress(), _serverSocketChannel(INVALID_SOCKET), _receiveBufferSize(receiveBufferSize), @@ -207,12 +207,11 @@ namespace pvAccess { /** * Create transport, it registers itself to the registry. */ - std::auto_ptr responseHandler = _responseHandlerFactory->createResponseHandler(); detail::BlockingServerTCPTransportCodec::shared_pointer transport = detail::BlockingServerTCPTransportCodec::create( _context, newClient, - responseHandler, + _responseHandler, _socketSendBufferSize, _receiveBufferSize); diff --git a/src/remote/blockingTCPConnector.cpp b/src/remote/blockingTCPConnector.cpp index 87711f1..935160e 100644 --- a/src/remote/blockingTCPConnector.cpp +++ b/src/remote/blockingTCPConnector.cpp @@ -68,7 +68,7 @@ namespace epics { } Transport::shared_pointer BlockingTCPConnector::connect(TransportClient::shared_pointer const & client, - std::auto_ptr& responseHandler, osiSockAddr& address, + ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& address, int8 transportRevision, int16 priority) { SOCKET socket = INVALID_SOCKET; diff --git a/src/remote/blockingUDP.h b/src/remote/blockingUDP.h index 682ccec..7feca9f 100644 --- a/src/remote/blockingUDP.h +++ b/src/remote/blockingUDP.h @@ -47,12 +47,12 @@ namespace epics { POINTER_DEFINITIONS(BlockingUDPTransport); BlockingUDPTransport(bool serverFlag, - std::auto_ptr &responseHandler, + ResponseHandler::shared_pointer const & responseHandler, SOCKET channel, osiSockAddr &bindAddress, short remoteTransportRevision); static shared_pointer create(bool serverFlag, - std::auto_ptr& responseHandler, + ResponseHandler::shared_pointer const & responseHandler, SOCKET channel, osiSockAddr& bindAddress, short remoteTransportRevision) EPICS_DEPRECATED { @@ -314,7 +314,7 @@ namespace epics { /** * Response handler. */ - const std::auto_ptr _responseHandler; + ResponseHandler::shared_pointer _responseHandler; virtual void run(); @@ -419,7 +419,7 @@ namespace epics { * NOTE: transport client is ignored for broadcast (UDP). */ virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client, - std::auto_ptr& responseHandler, osiSockAddr& bindAddress, + ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& bindAddress, epics::pvData::int8 transportRevision, epics::pvData::int16 priority); private: diff --git a/src/remote/blockingUDPConnector.cpp b/src/remote/blockingUDPConnector.cpp index 676ce47..7bb8a01 100644 --- a/src/remote/blockingUDPConnector.cpp +++ b/src/remote/blockingUDPConnector.cpp @@ -19,7 +19,7 @@ namespace epics { namespace pvAccess { Transport::shared_pointer BlockingUDPConnector::connect(TransportClient::shared_pointer const & /*client*/, - auto_ptr& responseHandler, osiSockAddr& bindAddress, + ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& bindAddress, int8 transportRevision, int16 /*priority*/) { LOG(logLevelDebug, "Creating datagram socket to: %s.", diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index 7caa4a4..c43d58e 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -41,7 +41,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so PVACCESS_REFCOUNT_MONITOR_DEFINE(blockingUDPTransport); BlockingUDPTransport::BlockingUDPTransport(bool serverFlag, - auto_ptr& responseHandler, SOCKET channel, + ResponseHandler::shared_pointer const & responseHandler, SOCKET channel, osiSockAddr& bindAddress, short /*remoteTransportRevision*/) : _closed(), diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index f886c0a..83fdfdb 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -1442,7 +1442,7 @@ namespace epics { BlockingServerTCPTransportCodec::BlockingServerTCPTransportCodec( Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, + ResponseHandler::shared_pointer const & responseHandler, int32_t sendBufferSize, int32_t receiveBufferSize) : BlockingTCPTransportCodec(true, context, channel, responseHandler, @@ -1702,7 +1702,7 @@ namespace epics { BlockingClientTCPTransportCodec::BlockingClientTCPTransportCodec( Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, + ResponseHandler::shared_pointer const & responseHandler, int32_t sendBufferSize, int32_t receiveBufferSize, TransportClient::shared_pointer const & client, diff --git a/src/remote/codec.h b/src/remote/codec.h index 07a7410..b9371ce 100644 --- a/src/remote/codec.h +++ b/src/remote/codec.h @@ -481,7 +481,7 @@ namespace epics { bool serverFlag, Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, + ResponseHandler::shared_pointer const & responseHandler, int32_t sendBufferSize, int32_t receiveBufferSize, epics::pvData::int16 priority @@ -505,7 +505,7 @@ namespace epics { private: - std::auto_ptr _responseHandler; + ResponseHandler::shared_pointer _responseHandler; size_t _remoteTransportReceiveBufferSize; epics::pvData::int8 _remoteTransportRevision; epics::pvData::int16 _priority; @@ -529,7 +529,7 @@ namespace epics { BlockingServerTCPTransportCodec( Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, + ResponseHandler::shared_pointer const & responseHandler, int32_t sendBufferSize, int32_t receiveBufferSize ); @@ -537,7 +537,7 @@ namespace epics { static shared_pointer create( Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, + ResponseHandler::shared_pointer const & responseHandler, int sendBufferSize, int receiveBufferSize) { @@ -658,7 +658,7 @@ namespace epics { BlockingClientTCPTransportCodec( Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, + ResponseHandler::shared_pointer const & responseHandler, int32_t sendBufferSize, int32_t receiveBufferSize, TransportClient::shared_pointer const & client, @@ -670,7 +670,7 @@ namespace epics { static shared_pointer create( Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, + ResponseHandler::shared_pointer const & responseHandler, int32_t sendBufferSize, int32_t receiveBufferSize, TransportClient::shared_pointer const & client, diff --git a/src/remote/remote.h b/src/remote/remote.h index c05306e..acb3a9c 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -460,7 +460,7 @@ namespace epics { * @return transport instance. */ virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client, - std::auto_ptr& responseHandler, osiSockAddr& address, + ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& address, epics::pvData::int8 transportRevision, epics::pvData::int16 priority) = 0; }; diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 135d371..1810b98 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -4436,6 +4436,8 @@ namespace epics { m_connector.reset(new BlockingTCPConnector(thisPointer, m_receiveBufferSize, m_connectionTimeout)); m_transportRegistry.reset(new TransportRegistry()); + m_responseHandler.reset(new ClientResponseHandler(shared_from_this())); + // preinitialize security plugins SecurityPluginRegistry::instance(); @@ -4494,10 +4496,9 @@ namespace epics { TransportClient::shared_pointer nullTransportClient; - auto_ptr clientResponseHandler(new ClientResponseHandler(thisPointer)); auto_ptr broadcastConnector(new BlockingUDPConnector(false, true, true)); m_broadcastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, clientResponseHandler, + nullTransportClient, m_responseHandler, listenLocalAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); if (!m_broadcastTransport.get()) @@ -4510,10 +4511,9 @@ namespace epics { undefinedAddress.ia.sin_port = htons(0); undefinedAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); - clientResponseHandler.reset(new ClientResponseHandler(thisPointer)); auto_ptr searchConnector(new BlockingUDPConnector(false, false, true)); m_searchTransport = static_pointer_cast(searchConnector->connect( - nullTransportClient, clientResponseHandler, + nullTransportClient, m_responseHandler, undefinedAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); if (!m_searchTransport.get()) @@ -4795,9 +4795,7 @@ namespace epics { { try { - // TODO we are creating a new response handler even-though we might not need a new transprot !!! - auto_ptr handler(new ClientResponseHandler(shared_from_this())); - Transport::shared_pointer t = m_connector->connect(client, handler, *serverAddress, minorRevision, priority); + Transport::shared_pointer t = m_connector->connect(client, m_responseHandler, *serverAddress, minorRevision, priority); // TODO !!! //static_pointer_cast(t)->setFlushStrategy(m_flushStrategy); return t; @@ -4988,6 +4986,11 @@ namespace epics { */ TransportRegistry::shared_pointer m_transportRegistry; + /** + * Response handler. + */ + ClientResponseHandler::shared_pointer m_responseHandler; + /** * Context instance. */ diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 6ec64dc..3d44491 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -252,12 +252,6 @@ void ServerContextImpl::initialize(ChannelProviderRegistry::shared_pointer const _state = INITIALIZED; } -std::auto_ptr ServerContextImpl::createResponseHandler() -{ - ServerContextImpl::shared_pointer thisContext = shared_from_this(); - return std::auto_ptr(new ServerResponseHandler(thisContext)); -} - void ServerContextImpl::internalInitialize() { osiSockAttach(); @@ -266,8 +260,9 @@ void ServerContextImpl::internalInitialize() _transportRegistry.reset(new TransportRegistry()); ServerContextImpl::shared_pointer thisServerContext = shared_from_this(); + _responseHandler.reset(new ServerResponseHandler(thisServerContext)); - _acceptor.reset(new BlockingTCPAcceptor(thisServerContext, thisServerContext, _ifaceAddr, _receiveBufferSize)); + _acceptor.reset(new BlockingTCPAcceptor(thisServerContext, _responseHandler, _ifaceAddr, _receiveBufferSize)); _serverPort = ntohs(_acceptor->getBindAddress()->ia.sin_port); // setup broadcast UDP transport @@ -300,9 +295,8 @@ void ServerContextImpl::initializeBroadcastTransport() TransportClient::shared_pointer nullTransportClient; auto_ptr broadcastConnector(new BlockingUDPConnector(true, true, true)); - auto_ptr responseHandler = createResponseHandler(); _broadcastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, responseHandler, + nullTransportClient, _responseHandler, listenLocalAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); listenLocalAddress = *_broadcastTransport->getRemoteAddress(); @@ -323,9 +317,8 @@ void ServerContextImpl::initializeBroadcastTransport() */ _ifaceBCast.ia.sin_port = listenLocalAddress.ia.sin_port; - responseHandler = createResponseHandler(); _broadcastTransport2 = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, responseHandler, + nullTransportClient, _responseHandler, _ifaceBCast, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); /* The other wrinkle is that nothing should be sent from this second @@ -382,9 +375,8 @@ void ServerContextImpl::initializeBroadcastTransport() anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); // NOTE: localMulticastTransport is not started (no read is called on a socket) - auto_ptr responseHandler2 = createResponseHandler(); _localMulticastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, responseHandler2, + nullTransportClient, _responseHandler, anyAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); _localMulticastTransport->setMutlicastNIF(loAddr, true); @@ -452,7 +444,7 @@ void ServerContextImpl::run(int32 seconds) } // run... - _beaconEmitter->start(); + _beaconEmitter->start(); //TODO review this if(seconds == 0) diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 3d22720..4e92ecc 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -110,7 +110,6 @@ public: class epicsShareClass ServerContextImpl : public ServerContext, public Context, - public ResponseHandlerFactory, public std::tr1::enable_shared_from_this { public: @@ -143,7 +142,6 @@ public: TransportRegistry::shared_pointer getTransportRegistry(); std::map >& getSecurityPlugins(); - std::auto_ptr createResponseHandler(); virtual void newServerDetected(); @@ -376,7 +374,12 @@ private: */ TransportRegistry::shared_pointer _transportRegistry; - /** + /** + * Response handler. + */ + ResponseHandler::shared_pointer _responseHandler; + + /** * Channel access. */ ChannelProviderRegistry::shared_pointer _channelProviderRegistry; diff --git a/testApp/remote/testServer.cpp b/testApp/remote/testServer.cpp index 5ff0e84..6400478 100644 --- a/testApp/remote/testServer.cpp +++ b/testApp/remote/testServer.cpp @@ -2881,6 +2881,7 @@ int main(int argc, char *argv[]) .push_map() .build())); TestServer::ctx = srv; + srv->context->printInfo(); srv->start(true); cout << "Done" << endl; From 523af71b7f125df76c52100be2c01f0425452ba8 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 17 Dec 2015 14:02:11 +0100 Subject: [PATCH 32/51] duplicate search responses from the same server bound to multiple NIFs filtered out --- src/remote/channelSearchManager.h | 6 +++-- src/remote/simpleChannelSearchManagerImpl.cpp | 8 +++---- src/remote/simpleChannelSearchManagerImpl.h | 3 ++- src/remoteClient/clientContextImpl.cpp | 23 ++++++++++++++----- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/remote/channelSearchManager.h b/src/remote/channelSearchManager.h index 5f5458d..78fac05 100644 --- a/src/remote/channelSearchManager.h +++ b/src/remote/channelSearchManager.h @@ -41,11 +41,12 @@ class SearchInstance { /** * Search response from server (channel found). + * @param guid server GUID. * @param minorRevision server minor PVA revision. * @param serverAddress server address. */ // TODO make serverAddress an URI or similar - virtual void searchResponse(int8_t minorRevision, osiSockAddr* serverAddress) = 0; + virtual void searchResponse(const GUID & guid, int8_t minorRevision, osiSockAddr* serverAddress) = 0; }; class ChannelSearchManager { @@ -78,12 +79,13 @@ class ChannelSearchManager { /** * Search response from server (channel found). + * @param guid server GUID. * @param cid client channel ID. * @param seqNo search sequence number. * @param minorRevision server minor PVA revision. * @param serverAddress server address. */ - virtual void searchResponse(pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress) = 0; + virtual void searchResponse(const GUID & guid, pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress) = 0; /** * New server detected. diff --git a/src/remote/simpleChannelSearchManagerImpl.cpp b/src/remote/simpleChannelSearchManagerImpl.cpp index 5363ca1..2e61abf 100644 --- a/src/remote/simpleChannelSearchManagerImpl.cpp +++ b/src/remote/simpleChannelSearchManagerImpl.cpp @@ -137,7 +137,7 @@ void SimpleChannelSearchManagerImpl::unregisterSearchInstance(SearchInstance::sh m_channels.erase(id); } -void SimpleChannelSearchManagerImpl::searchResponse(pvAccessID cid, int32_t /*seqNo*/, int8_t minorRevision, osiSockAddr* serverAddress) +void SimpleChannelSearchManagerImpl::searchResponse(const GUID & guid, pvAccessID cid, int32_t /*seqNo*/, int8_t minorRevision, osiSockAddr* serverAddress) { Lock guard(m_channelMutex); std::map::iterator channelsIter = m_channels.find(cid); @@ -145,10 +145,10 @@ void SimpleChannelSearchManagerImpl::searchResponse(pvAccessID cid, int32_t /*se { guard.unlock(); - // minor hack to enable duplicate reports + // enable duplicate reports SearchInstance::shared_pointer si = std::tr1::dynamic_pointer_cast(m_context.lock()->getChannel(cid)); if (si) - si->searchResponse(minorRevision, serverAddress); + si->searchResponse(guid, minorRevision, serverAddress); } else { @@ -160,7 +160,7 @@ void SimpleChannelSearchManagerImpl::searchResponse(pvAccessID cid, int32_t /*se guard.unlock(); // then notify SearchInstance - si->searchResponse(minorRevision, serverAddress); + si->searchResponse(guid, minorRevision, serverAddress); } } diff --git a/src/remote/simpleChannelSearchManagerImpl.h b/src/remote/simpleChannelSearchManagerImpl.h index cead8ff..060dc48 100644 --- a/src/remote/simpleChannelSearchManagerImpl.h +++ b/src/remote/simpleChannelSearchManagerImpl.h @@ -89,12 +89,13 @@ class SimpleChannelSearchManagerImpl : void unregisterSearchInstance(SearchInstance::shared_pointer const & channel); /** * Search response from server (channel found). + * @param guid server GUID. * @param cid client channel ID. * @param seqNo search sequence number. * @param minorRevision server minor PVA revision. * @param serverAddress server address. */ - void searchResponse(pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress); + void searchResponse(const GUID & guid, pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress); /** * New server detected. * Boost searching of all channels. diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 1810b98..adea100 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -2862,7 +2862,7 @@ namespace epics { { transport->ensureData(4); pvAccessID cid = payloadBuffer->getInt(); - csm->searchResponse(cid, searchSequenceId, version, &serverAddress); + csm->searchResponse(guid, cid, searchSequenceId, version, &serverAddress); } @@ -3497,6 +3497,11 @@ namespace epics { /// Used by SearchInstance. int32_t m_userValue; + /** + * @brief Server GUID. + */ + GUID m_guid; + /** * Constructor. * @param context @@ -3931,21 +3936,23 @@ namespace epics { m_addressIndex = m_addresses->size()*STATIC_SEARCH_MAX_MULTIPLIER; // NOTE: calls channelConnectFailed() on failure - searchResponse(PVA_PROTOCOL_REVISION, &((*m_addresses)[ix])); + static GUID guid = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; + searchResponse(guid, PVA_PROTOCOL_REVISION, &((*m_addresses)[ix])); } virtual void timerStopped() { // noop } - virtual void searchResponse(int8 minorRevision, osiSockAddr* serverAddress) { + virtual void searchResponse(const GUID & guid, int8 minorRevision, osiSockAddr* serverAddress) { Lock guard(m_channelMutex); Transport::shared_pointer transport = m_transport; if (transport.get()) { - // TODO use GUID to determine whether there are multiple servers with the same channel - // multiple defined PV or reconnect request (same server address) - if (!sockAddrAreIdentical(transport->getRemoteAddress(), serverAddress)) + // GUID check case: same server listening on different NIF + + if (!sockAddrAreIdentical(transport->getRemoteAddress(), serverAddress) && + !std::equal(guid.value, guid.value + 12, m_guid.value)) { EXCEPTION_GUARD(m_requester->message("More than one channel with name '" + m_name + "' detected, connected to: " + inetAddressToString(*transport->getRemoteAddress()) + ", ignored: " + inetAddressToString(*serverAddress), warningMessage)); @@ -3960,6 +3967,10 @@ namespace epics { return; } + + // remember GUID + std::copy(guid.value, guid.value + 12, m_guid.value); + // create channel createChannel(transport); } From fed3bba0dca3b298f4c8aa61a8cfd053fa52fe7c Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 17 Dec 2015 14:36:18 +0100 Subject: [PATCH 33/51] more descriptive error logs --- src/remote/blockingUDPTransport.cpp | 6 ++++-- src/server/serverContext.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index c43d58e..c981f97 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -364,7 +364,8 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so { char errStr[64]; epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); - LOG(logLevelDebug, "Socket sendto error: %s.", errStr); + LOG(logLevelDebug, "Socket sendto to %s error: %s.", + inetAddressToString(address).c_str(), errStr); return false; } @@ -395,7 +396,8 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so { char errStr[64]; epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); - LOG(logLevelDebug, "Socket sendto error: %s.", errStr); + LOG(logLevelDebug, "Socket sendto to %s error: %s.", + inetAddressToString((*_sendAddresses)[i]).c_str(), errStr); allOK = false; } } diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 3d44491..9935b6f 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -138,6 +138,7 @@ void ServerContextImpl::loadConfiguration() if (debugLevel > 0) SET_LOG_LEVEL(logLevelDebug); + // TODO multiple addresses _ifaceAddr.ia.sin_family = AF_INET; _ifaceAddr.ia.sin_addr.s_addr = htonl(INADDR_ANY); _ifaceAddr.ia.sin_port = 0; @@ -307,8 +308,10 @@ void ServerContextImpl::initializeBroadcastTransport() if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { if(_ifaceBCast.ia.sin_family == AF_UNSPEC || _ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { - LOG(logLevelWarn, "Unable to find broadcast address of interface\n"); - } else { + LOG(logLevelInfo, "Unable to find broadcast address of interface %s, using it as unicast address.", inetAddressToString(_ifaceBCast, false).c_str()); + } + //else + { /* An oddness of BSD sockets (not winsock) is that binding to * INADDR_ANY will receive unicast and broadcast, but binding to * a specific interface address receives only unicast. The trick @@ -591,7 +594,8 @@ void ServerContextImpl::printInfo(ostream& str) << "SERVER_PORT : " << _serverPort << endl \ << "RCV_BUFFER_SIZE : " << _receiveBufferSize << endl \ << "IGNORE_ADDR_LIST: " << _ignoreAddressList << endl \ - << "STATE : " << ServerContextImpl::StateNames[_state] << endl; + << "INTF_ADDR_LIST : " << inetAddressToString(_ifaceAddr, false) << endl \ + << "STATE : " << ServerContextImpl::StateNames[_state] << endl; } void ServerContextImpl::dispose() From 52161dff606988e373683155ab3438208fa65fa5 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 17 Dec 2015 22:05:25 +0100 Subject: [PATCH 34/51] udp cntd --- src/server/serverContext.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 9935b6f..33d99ce 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -308,9 +308,9 @@ void ServerContextImpl::initializeBroadcastTransport() if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { if(_ifaceBCast.ia.sin_family == AF_UNSPEC || _ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { - LOG(logLevelInfo, "Unable to find broadcast address of interface %s, using it as unicast address.", inetAddressToString(_ifaceBCast, false).c_str()); + LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(_ifaceBCast, false).c_str()); } - //else + else { /* An oddness of BSD sockets (not winsock) is that binding to * INADDR_ANY will receive unicast and broadcast, but binding to @@ -369,6 +369,21 @@ void ServerContextImpl::initializeBroadcastTransport() try { osiSockAddr group; + + // TODO there should be different mcast groups + // one for all interfaces, and then one per interface + + // if received on specific NIF, then it's resent to speicfic mcast address + // if received on any NIF, then it should be resent to specific mcast address (calculate from receive from and mask) + // if interested for all the NIFs, then it should join to all specific mcast addresses + + // --- server + // UDP bind on broadcast port + mcast as above + + // -- client + // UDP bind to broadcast port + mcast as above + + aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); _broadcastTransport->join(group, _ifaceAddr); From 63a0c71fe9f68d16abd09406772f814969356775 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 31 Dec 2015 11:18:49 +0100 Subject: [PATCH 35/51] local multicast revised for multiple NIF (server side only) --- src/remote/blockingUDP.h | 19 ++++ src/remote/blockingUDPTransport.cpp | 7 +- src/remoteClient/clientContextImpl.cpp | 27 +++-- src/server/responseHandlers.cpp | 8 +- src/server/serverContext.cpp | 64 ++++++------ src/utils/inetAddressUtil.cpp | 139 +++++++++++++++++++++++++ src/utils/inetAddressUtil.h | 5 + 7 files changed, 227 insertions(+), 42 deletions(-) diff --git a/src/remote/blockingUDP.h b/src/remote/blockingUDP.h index 7feca9f..be9745a 100644 --- a/src/remote/blockingUDP.h +++ b/src/remote/blockingUDP.h @@ -194,6 +194,19 @@ namespace epics { _sendTo = sendTo; } + virtual void setLocalMulticastAddress(const osiSockAddr& sendTo) { + _localMulticastAddressEnabled = true; + _localMulticastAddress = sendTo; + } + + virtual bool hasLocalMulticastAddress() const { + return _localMulticastAddressEnabled; + } + + virtual const osiSockAddr& getLocalMulticastAddress() const { + return _localMulticastAddress; + } + virtual void flushSerializeBuffer() { // noop } @@ -366,6 +379,12 @@ namespace epics { osiSockAddr _sendTo; bool _sendToEnabled; + /** + * Local multicast address. + */ + osiSockAddr _localMulticastAddress; + bool _localMulticastAddressEnabled; + /** * Receive buffer. */ diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index c981f97..a2f9f9c 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -389,6 +389,9 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so (target == inetAddressType_broadcast_multicast && _isSendAddressUnicast[i])) continue; + LOG(logLevelDebug, "Sending to %d bytes to %s.", + buffer->getRemaining(), inetAddressToString((*_sendAddresses)[i]).c_str()); + int retval = sendto(_channel, buffer->getArray(), buffer->getLimit(), 0, &((*_sendAddresses)[i].sa), sizeof(sockaddr)); @@ -460,7 +463,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so char errStr[64]; epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); throw std::runtime_error( - string("Failed to set multicast network inteface '") + + string("Failed to set multicast network interface '") + inetAddressToString(nifAddr, false) + "': " + errStr); } @@ -473,7 +476,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so char errStr[64]; epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); throw std::runtime_error( - string("Failed to enable multicast loopback on network inteface '") + + string("Failed to enable multicast loopback on network interface '") + inetAddressToString(nifAddr, false) + "': " + errStr); } diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index adea100..83d36bb 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -4468,10 +4468,24 @@ namespace epics { */ bool initializeUDPTransport() { + // where to bind (listen) address + osiSockAddr listenLocalAddress; + listenLocalAddress.ia.sin_family = AF_INET; + listenLocalAddress.ia.sin_port = htons(m_broadcastPort); + listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + // quary broadcast addresses of all IFs SOCKET socket = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); if (socket == INVALID_SOCKET) return false; auto_ptr broadcastAddresses(getBroadcastAddresses(socket, m_broadcastPort)); + + int ifIndex = discoverInterfaceIndex(socket, &listenLocalAddress); + if (ifIndex == -1) + { + LOG(logLevelWarn, "Unable to find interface index for %s.", inetAddressToString(listenLocalAddress, false).c_str()); + // TODO fallback + } + epicsSocketDestroy (socket); // set broadcast address list @@ -4497,12 +4511,6 @@ namespace epics { LOG(logLevelDebug, "Broadcast address #%d: %s.", i, inetAddressToString((*broadcastAddresses)[i]).c_str()); - // where to bind (listen) address - osiSockAddr listenLocalAddress; - listenLocalAddress.ia.sin_family = AF_INET; - listenLocalAddress.ia.sin_port = htons(m_broadcastPort); - listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); - ClientContextImpl::shared_pointer thisPointer = shared_from_this(); TransportClient::shared_pointer nullTransportClient; @@ -4540,8 +4548,13 @@ namespace epics { { try { + int lastAddr = 128 + ifIndex; + std::ostringstream o; + // TODO configurable + o << "224.0.0." << lastAddr; + //osiSockAddr group; - aToIPAddr("224.0.0.128", m_broadcastPort, &m_localBroadcastAddress.ia); + aToIPAddr(o.str().c_str(), m_broadcastPort, &m_localBroadcastAddress.ia); m_broadcastTransport->join(m_localBroadcastAddress, loAddr); // NOTE: this disables usage of multicast addresses in EPICS_PVA_ADDR_LIST diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 7120101..05acf7a 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -263,10 +263,10 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, // // locally broadcast if unicast (qosCode & 0x80 == 0x80) // - if (0) + if ((qosCode & 0x80) == 0x80) { - BlockingUDPTransport::shared_pointer bt = _context->getLocalMulticastTransport(); - if (bt) + BlockingUDPTransport::shared_pointer bt = _context->getBroadcastTransport(); + if (bt && bt->hasLocalMulticastAddress()) { // clear unicast flag payloadBuffer->put(startPosition+4, (int8)(qosCode & ~0x80)); @@ -277,7 +277,7 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, payloadBuffer->setPosition(payloadBuffer->getLimit()); // send will call flip() - bt->send(payloadBuffer); + bt->send(payloadBuffer, bt->getLocalMulticastAddress()); return; } } diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 33d99ce..b3c4e77 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -291,6 +291,14 @@ void ServerContextImpl::initializeBroadcastTransport() THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); } auto_ptr broadcastAddresses(getBroadcastAddresses(socket,_broadcastPort)); + + int ifIndex = discoverInterfaceIndex(socket, &listenLocalAddress); + if (ifIndex == -1) + { + LOG(logLevelWarn, "Unable to find interface index for %s.", inetAddressToString(listenLocalAddress, false).c_str()); + // TODO fallback + } + epicsSocketDestroy(socket); TransportClient::shared_pointer nullTransportClient; @@ -360,50 +368,48 @@ void ServerContextImpl::initializeBroadcastTransport() } - // setup local broadcasting + // + // Setup local broadcasting + // + // Each network interface gets its own multicast group on a local interface. + // Multicast address is determined by prefix 224.0.0.124 + NIF index + // + // TODO configurable local NIF, address osiSockAddr loAddr; getLoopbackNIF(loAddr, "", 0); + + osiSockAddr group; + int lastAddr = 128 + ifIndex; + std::ostringstream o; + // TODO configurable prefix and base + o << "224.0.0." << lastAddr; + aToIPAddr(o.str().c_str(), _broadcastPort, &group.ia); + + _broadcastTransport->setMutlicastNIF(loAddr, true); + _broadcastTransport->setLocalMulticastAddress(group); + if (true) { try { - osiSockAddr group; - - // TODO there should be different mcast groups - // one for all interfaces, and then one per interface - - // if received on specific NIF, then it's resent to speicfic mcast address - // if received on any NIF, then it should be resent to specific mcast address (calculate from receive from and mask) - // if interested for all the NIFs, then it should join to all specific mcast addresses - - // --- server - // UDP bind on broadcast port + mcast as above - - // -- client - // UDP bind to broadcast port + mcast as above - - - aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); - _broadcastTransport->join(group, _ifaceAddr); - - osiSockAddr anyAddress; - anyAddress.ia.sin_family = AF_INET; - anyAddress.ia.sin_port = htons(0); - anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); - - // NOTE: localMulticastTransport is not started (no read is called on a socket) + // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address _localMulticastTransport = static_pointer_cast(broadcastConnector->connect( nullTransportClient, _responseHandler, - anyAddress, PVA_PROTOCOL_REVISION, + group, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); + _localMulticastTransport->join(group, loAddr); + /* used for sending _localMulticastTransport->setMutlicastNIF(loAddr, true); InetAddrVector sendAddressList; sendAddressList.push_back(group); _localMulticastTransport->setSendAddresses(&sendAddressList); + */ - LOG(logLevelDebug, "Local multicast enabled on %s using network interface %s.", - inetAddressToString(group).c_str(), inetAddressToString(loAddr, false).c_str()); + LOG(logLevelDebug, "Local multicast for %s enabled on %s/%s.", + inetAddressToString(listenLocalAddress, false).c_str(), + inetAddressToString(loAddr, false).c_str(), + inetAddressToString(group).c_str()); } catch (std::exception& ex) { diff --git a/src/utils/inetAddressUtil.cpp b/src/utils/inetAddressUtil.cpp index 24301f6..27cbde5 100644 --- a/src/utils/inetAddressUtil.cpp +++ b/src/utils/inetAddressUtil.cpp @@ -212,5 +212,144 @@ int getLoopbackNIF(osiSockAddr &loAddr, string const & localNIF, unsigned short return 1; } + + +// copy of base-3.14.12.4/src/libCom/osi/os/default/osdNetIntf.c +// TODO support windows (see osi/os/WIN32/osdNetIntf.c) + +#include +//#include +#include + +/* + * Determine the size of an ifreq structure + * Made difficult by the fact that addresses larger than the structure + * size may be returned from the kernel. + */ +static size_t ifreqSize ( struct ifreq *pifreq ) +{ + size_t size; + + size = ifreq_size ( pifreq ); + if ( size < sizeof ( *pifreq ) ) { + size = sizeof ( *pifreq ); + } + return size; +} + +/* + * Move to the next ifreq structure + */ +static struct ifreq * ifreqNext ( struct ifreq *pifreq ) +{ + struct ifreq *ifr; + + ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq ); + return ifr; +} + +int discoverInterfaceIndex + (SOCKET socket, const osiSockAddr *pMatchAddr) +{ + static const unsigned nelem = 100; + int status; + struct ifconf ifconf; + struct ifreq *pIfreqList; + struct ifreq *pIfreqListEnd; + struct ifreq *pifreq; + struct ifreq *pnextifreq; + + /* + * check if pMatchAddr is valid + */ + if ( pMatchAddr->sa.sa_family == AF_UNSPEC || + pMatchAddr->sa.sa_family != AF_INET || + pMatchAddr->ia.sin_addr.s_addr == htonl(INADDR_ANY) ) { + errlogPrintf ("osiSockDiscoverInterfaceIndex(): invalid pMatchAddr\n"); + return -1; + } + + /* + * use pool so that we avoid using too much stack space + * + * nelem is set to the maximum interfaces + * on one machine here + */ + pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) ); + if (!pIfreqList) { + errlogPrintf ("osiSockDiscoverInterfaceIndex(): no memory to complete request\n"); + return -1; + } + + ifconf.ifc_len = nelem * sizeof(*pifreq); + ifconf.ifc_req = pIfreqList; + status = socket_ioctl (socket, SIOCGIFCONF, &ifconf); + if (status < 0 || ifconf.ifc_len == 0) { + errlogPrintf ("osiSockDiscoverInterfaceIndex(): unable to fetch network interface configuration\n"); + free (pIfreqList); + return -1; + } + + pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList); + pIfreqListEnd--; + + for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { + uint32_t current_ifreqsize; + + /* + * find the next ifreq + */ + pnextifreq = ifreqNext (pifreq); + + /* determine ifreq size */ + current_ifreqsize = ifreqSize ( pifreq ); + /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */ + memmove(pIfreqList, pifreq, current_ifreqsize); + + /* + * If its not an internet interface then dont use it + */ + if ( pIfreqList->ifr_addr.sa_family != AF_INET ) { + continue; + } + + /* + * if it isnt a wildcarded interface then look for + * an exact match + */ + struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr; + if ( pInetAddr->sin_addr.s_addr == pMatchAddr->ia.sin_addr.s_addr ) { + + unsigned int index = if_nametoindex(pIfreqList->ifr_name); + if ( !index ) { + errlogPrintf ("osiSockDiscoverInterfaceIndex(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); + free (pIfreqList); + return -1; + } + + free (pIfreqList); + return index; + + /* + status = socket_ioctl ( socket, SIOCGIFINDEX, pIfreqList ); + if ( status ) { + errlogPrintf ("osiSockDiscoverInterfaceIndex(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); + free (pIfreqList); + return -1; + } + + free (pIfreqList); + return pIfreqList->ifr_ifindex; + */ + } + + } + + /* not found */ + free ( pIfreqList ); + return -1; +} + + } } diff --git a/src/utils/inetAddressUtil.h b/src/utils/inetAddressUtil.h index 57982af..b46b736 100644 --- a/src/utils/inetAddressUtil.h +++ b/src/utils/inetAddressUtil.h @@ -42,6 +42,11 @@ namespace pvAccess { */ epicsShareFunc InetAddrVector* getBroadcastAddresses(SOCKET sock, unsigned short defaultPort); + /** + * Returns NIF index for given interface address, or -1 on failure. + */ + epicsShareFunc int discoverInterfaceIndex(SOCKET socket, const osiSockAddr *pMatchAddr); + /** * Encode IPv4 address as IPv6 address. * @param buffer byte-buffer where to put encoded data. From 708379ec0ac76b814e2bd85c667727f83a42d324 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 31 Dec 2015 11:53:12 +0100 Subject: [PATCH 36/51] udp init cleanup, auto beacon addr list --- src/remote/blockingUDPTransport.cpp | 1 + src/server/serverContext.cpp | 91 +++++++++++++++++++---------- 2 files changed, 61 insertions(+), 31 deletions(-) diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index a2f9f9c..8296d56 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -51,6 +51,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so _sendAddresses(0), _ignoredAddresses(0), _sendToEnabled(false), + _localMulticastAddressEnabled(false), _receiveBuffer(new ByteBuffer(MAX_UDP_RECV)), _sendBuffer(new ByteBuffer(MAX_UDP_RECV)), _lastMessageStartPosition(0), diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index b3c4e77..44f4cff 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -284,22 +284,6 @@ void ServerContextImpl::initializeBroadcastTransport() listenLocalAddress.ia.sin_port = htons(_broadcastPort); listenLocalAddress.ia.sin_addr.s_addr = _ifaceAddr.ia.sin_addr.s_addr; - // where to send addresses - SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (socket == INVALID_SOCKET) - { - THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); - } - auto_ptr broadcastAddresses(getBroadcastAddresses(socket,_broadcastPort)); - - int ifIndex = discoverInterfaceIndex(socket, &listenLocalAddress); - if (ifIndex == -1) - { - LOG(logLevelWarn, "Unable to find interface index for %s.", inetAddressToString(listenLocalAddress, false).c_str()); - // TODO fallback - } - - epicsSocketDestroy(socket); TransportClient::shared_pointer nullTransportClient; @@ -309,15 +293,15 @@ void ServerContextImpl::initializeBroadcastTransport() listenLocalAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); listenLocalAddress = *_broadcastTransport->getRemoteAddress(); - _broadcastTransport->setSendAddresses(broadcastAddresses.get()); _broadcastPort = ntohs(listenLocalAddress.ia.sin_port); + _ifaceBCast.ia.sin_port = listenLocalAddress.ia.sin_port; -#if !defined(_WIN32) if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { if(_ifaceBCast.ia.sin_family == AF_UNSPEC || _ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(_ifaceBCast, false).c_str()); } +#if !defined(_WIN32) else { /* An oddness of BSD sockets (not winsock) is that binding to @@ -326,8 +310,6 @@ void ServerContextImpl::initializeBroadcastTransport() * is to bind a second socket to the interface broadcast address, * which will then receive only broadcasts. */ - _ifaceBCast.ia.sin_port = listenLocalAddress.ia.sin_port; - _broadcastTransport2 = static_pointer_cast(broadcastConnector->connect( nullTransportClient, _responseHandler, _ifaceBCast, PVA_PROTOCOL_REVISION, @@ -340,17 +322,41 @@ void ServerContextImpl::initializeBroadcastTransport() } #endif - // set ignore address list - if (!_ignoreAddressList.empty()) - { - // we do not care about the port - auto_ptr list(getSocketAddressList(_ignoreAddressList, 0, NULL)); - if (list.get() != NULL && list->size() > 0) - { - _broadcastTransport->setIgnoredAddresses(list.get()); - } - } - // set broadcast address list + + SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (socket == INVALID_SOCKET) + { + THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); + } + + + auto_ptr broadcastAddresses; + if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) + { + InetAddrVector * v = new InetAddrVector; + v->push_back(_ifaceBCast); + broadcastAddresses.reset(v); + } + else + { + // all the interfaces + broadcastAddresses.reset(getBroadcastAddresses(socket, _broadcastPort)); + } + + int ifIndex = discoverInterfaceIndex(socket, &listenLocalAddress); + if (ifIndex == -1) + { + LOG(logLevelWarn, "Unable to find interface index for %s.", inetAddressToString(listenLocalAddress, false).c_str()); + // TODO fallback + } + + epicsSocketDestroy(socket); + + + // set default (auto) address list + _broadcastTransport->setSendAddresses(broadcastAddresses.get()); + + // set broadcast address list if (!_beaconAddressList.empty()) { // if auto is true, add it to specified list @@ -368,6 +374,29 @@ void ServerContextImpl::initializeBroadcastTransport() } + // debug output for broadcast addresses + InetAddrVector* blist = _broadcastTransport->getSendAddresses(); + if (!blist || !blist->size()) + LOG(logLevelWarn, + "No broadcast addresses found or specified!"); + else + for (size_t i = 0; i < blist->size(); i++) + LOG(logLevelDebug, + "Broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); + + // + // set ignore address list + // + if (!_ignoreAddressList.empty()) + { + // we do not care about the port + auto_ptr list(getSocketAddressList(_ignoreAddressList, 0, NULL)); + if (list.get() != NULL && list->size() > 0) + { + _broadcastTransport->setIgnoredAddresses(list.get()); + } + } + // // Setup local broadcasting // From 21a1dad07f4e92bf8f045b70983adbb287a8c44f Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Mon, 4 Jan 2016 14:12:19 +0100 Subject: [PATCH 37/51] server: multiple NIF support for UDP --- src/server/responseHandlers.cpp | 6 +- src/server/serverContext.cpp | 452 +++++++++++++++++--------------- src/server/serverContext.h | 30 ++- src/utils/inetAddressUtil.cpp | 147 ++++++++--- src/utils/inetAddressUtil.h | 7 + 5 files changed, 370 insertions(+), 272 deletions(-) diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 05acf7a..ba06d17 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -258,14 +258,12 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, // TODO DoS attack? const bool responseRequired = (QOS_REPLY_REQUIRED & qosCode) != 0; - // TODO bloom filter or similar server selection (by GUID) - // - // locally broadcast if unicast (qosCode & 0x80 == 0x80) + // locally broadcast if unicast (qosCode & 0x80 == 0x80) via UDP // if ((qosCode & 0x80) == 0x80) { - BlockingUDPTransport::shared_pointer bt = _context->getBroadcastTransport(); + BlockingUDPTransport::shared_pointer bt = dynamic_pointer_cast(transport); if (bt && bt->hasLocalMulticastAddress()) { // clear unicast flag diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 44f4cff..f30bd35 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -33,7 +33,6 @@ ServerContextImpl::ServerContextImpl(): _serverPort(PVA_SERVER_PORT), _receiveBufferSize(MAX_TCP_RECV), _timer(), - _broadcastTransport(), _beaconEmitter(), _acceptor(), _transportRegistry(), @@ -166,20 +165,19 @@ void ServerContextImpl::loadConfiguration() _channelProviderNames = config->getPropertyAsString("EPICS_PVA_PROVIDER_NAMES", _channelProviderNames); _channelProviderNames = config->getPropertyAsString("EPICS_PVAS_PROVIDER_NAMES", _channelProviderNames); - _ifaceBCast.ia.sin_family = AF_UNSPEC; - if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { - ELLLIST alist = ELLLIST_INIT; - SOCKET sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); - if(sock) { - osiSockDiscoverBroadcastAddresses(&alist, sock, &_ifaceAddr); - epicsSocketDestroy(sock); - } - if(ellCount(&alist)>0) { - osiSockAddrNode *node = (osiSockAddrNode*)ellFirst(&alist); - _ifaceBCast = node->addr; - } - ellFree(&alist); + SOCKET sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); + if (!sock) { + THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); + return; } + + if (discoverInterfaces(_ifaceList, sock, &_ifaceAddr) || _ifaceList.size() == 0) + { + THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport, no interfaces available."); + return; + } + + epicsSocketDestroy(sock); } bool ServerContextImpl::isChannelProviderNamePreconfigured() @@ -269,201 +267,230 @@ void ServerContextImpl::internalInitialize() // setup broadcast UDP transport initializeBroadcastTransport(); - // TODO introduce a constant + // TODO introduce "tcp" a constant _beaconEmitter.reset(new BeaconEmitter("tcp", _broadcastTransport, thisServerContext)); } void ServerContextImpl::initializeBroadcastTransport() { - // setup UDP transport - try - { - // where to bind (listen) address - osiSockAddr listenLocalAddress; - listenLocalAddress.ia.sin_family = AF_INET; - listenLocalAddress.ia.sin_port = htons(_broadcastPort); - listenLocalAddress.ia.sin_addr.s_addr = _ifaceAddr.ia.sin_addr.s_addr; + TransportClient::shared_pointer nullTransportClient; + auto_ptr broadcastConnector(new BlockingUDPConnector(true, true, true)); + + osiSockAddr anyAddress; + anyAddress.ia.sin_family = AF_INET; + anyAddress.ia.sin_port = htons(0); + anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + + _broadcastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + anyAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + + // + // set broadcast (send) addresses - where to send beacons + // + + InetAddrVector autoBCastAddr; + for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) + { + ifaceNode node = *iter; + + if (node.ifaceBCast.ia.sin_family != AF_UNSPEC) + { + node.ifaceBCast.ia.sin_port = htons(_broadcastPort); + autoBCastAddr.push_back(node.ifaceBCast); + } + } + + // + // set beacon (broadcast) address list + // - TransportClient::shared_pointer nullTransportClient; + // set broadcast address list + if (!_beaconAddressList.empty()) + { + // if auto is true, add it to specified list + if (!_autoBeaconAddressList) + autoBCastAddr.clear(); - auto_ptr broadcastConnector(new BlockingUDPConnector(true, true, true)); - _broadcastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - listenLocalAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - listenLocalAddress = *_broadcastTransport->getRemoteAddress(); - _broadcastPort = ntohs(listenLocalAddress.ia.sin_port); - _ifaceBCast.ia.sin_port = listenLocalAddress.ia.sin_port; + auto_ptr list(getSocketAddressList(_beaconAddressList, _broadcastPort, &autoBCastAddr)); + if (list.get() && list->size()) + { + _broadcastTransport->setSendAddresses(list.get()); + } + else // TODO or no fallback at all + { + // fallback + // set default (auto) address list + _broadcastTransport->setSendAddresses(&autoBCastAddr); + } + } + else + { + // fallback + // set default (auto) address list + _broadcastTransport->setSendAddresses(&autoBCastAddr); + } - if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { - if(_ifaceBCast.ia.sin_family == AF_UNSPEC || - _ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { - LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(_ifaceBCast, false).c_str()); + + // debug output for broadcast addresses + InetAddrVector* blist = _broadcastTransport->getSendAddresses(); + if (!blist || !blist->size()) + LOG(logLevelWarn, + "No beacon broadcast addresses found or specified!"); + else + for (size_t i = 0; i < blist->size(); i++) + LOG(logLevelDebug, + "Beacon broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); + + // + // set ignore address list + // + auto_ptr ignoreAddressList; + if (!_ignoreAddressList.empty()) + { + // we do not care about the port + ignoreAddressList.reset(getSocketAddressList(_ignoreAddressList, 0, NULL)); + } + + + + // TODO configurable local NIF, address + osiSockAddr loAddr; + getLoopbackNIF(loAddr, "", 0); + + for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) + { + ifaceNode node = *iter; + + LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s, index %d.", + inetAddressToString(node.ifaceAddr, false).c_str(), + inetAddressToString(node.ifaceBCast, false).c_str(), + node.ifaceIndex); + try + { + // where to bind (listen) address + // TODO opt copy + osiSockAddr listenLocalAddress; + listenLocalAddress.ia.sin_family = AF_INET; + listenLocalAddress.ia.sin_port = htons(_broadcastPort); + listenLocalAddress.ia.sin_addr.s_addr = node.ifaceAddr.ia.sin_addr.s_addr; + + BlockingUDPTransport::shared_pointer transport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + listenLocalAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + listenLocalAddress = *transport->getRemoteAddress(); + + if (ignoreAddressList.get() && ignoreAddressList->size()) + transport->setIgnoredAddresses(ignoreAddressList.get()); + + + BlockingUDPTransport::shared_pointer transport2; + + if(node.ifaceBCast.ia.sin_family == AF_UNSPEC || + node.ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { + LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(node.ifaceBCast, false).c_str()); + } + #if !defined(_WIN32) + else + { + /* An oddness of BSD sockets (not winsock) is that binding to + * INADDR_ANY will receive unicast and broadcast, but binding to + * a specific interface address receives only unicast. The trick + * is to bind a second socket to the interface broadcast address, + * which will then receive only broadcasts. + */ + + // TODO opt copy + osiSockAddr bcastAddress; + bcastAddress.ia.sin_family = AF_INET; + bcastAddress.ia.sin_port = htons(_broadcastPort); + bcastAddress.ia.sin_addr.s_addr = node.ifaceBCast.ia.sin_addr.s_addr; + + transport2 = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + bcastAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + /* The other wrinkle is that nothing should be sent from this second + * socket. So replies are made through the unicast socket. + */ + transport2->setReplyTransport(transport); + + if (ignoreAddressList.get() && ignoreAddressList->size()) + transport2->setIgnoredAddresses(ignoreAddressList.get()); + } + #endif + + // TODO set ignore list on transport and transport2 + + + // + // Setup local broadcasting + // + // Each network interface gets its own multicast group on a local interface. + // Multicast address is determined by prefix 224.0.0.128 + NIF index + // + + osiSockAddr group; + int lastAddr = 128 + node.ifaceIndex; + std::ostringstream o; + // TODO configurable prefix and base + o << "224.0.0." << lastAddr; + aToIPAddr(o.str().c_str(), _broadcastPort, &group.ia); + + transport->setMutlicastNIF(loAddr, true); + transport->setLocalMulticastAddress(group); + + BlockingUDPTransport::shared_pointer localMulticastTransport; + + if (true) + { + try + { + // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address + localMulticastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + group, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + localMulticastTransport->join(group, loAddr); + + LOG(logLevelDebug, "Local multicast for %s enabled on %s/%s.", + inetAddressToString(listenLocalAddress, false).c_str(), + inetAddressToString(loAddr, false).c_str(), + inetAddressToString(group).c_str()); + } + catch (std::exception& ex) + { + LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); + } } -#if !defined(_WIN32) else { - /* An oddness of BSD sockets (not winsock) is that binding to - * INADDR_ANY will receive unicast and broadcast, but binding to - * a specific interface address receives only unicast. The trick - * is to bind a second socket to the interface broadcast address, - * which will then receive only broadcasts. - */ - _broadcastTransport2 = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - _ifaceBCast, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - /* The other wrinkle is that nothing should be sent from this second - * socket. So replies are made through the unicast socket. - */ - _broadcastTransport2->setReplyTransport(_broadcastTransport); + LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); } + + transport->start(); + if(transport2) + transport2->start(); + if (localMulticastTransport) + localMulticastTransport->start(); + + _udpTransports.push_back(transport); + _udpTransports.push_back(transport2); + _udpTransports.push_back(localMulticastTransport); + } -#endif - - - SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (socket == INVALID_SOCKET) + catch (std::exception& e) + { + THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e); + } + catch (...) { THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); } - - - auto_ptr broadcastAddresses; - if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) - { - InetAddrVector * v = new InetAddrVector; - v->push_back(_ifaceBCast); - broadcastAddresses.reset(v); - } - else - { - // all the interfaces - broadcastAddresses.reset(getBroadcastAddresses(socket, _broadcastPort)); - } - - int ifIndex = discoverInterfaceIndex(socket, &listenLocalAddress); - if (ifIndex == -1) - { - LOG(logLevelWarn, "Unable to find interface index for %s.", inetAddressToString(listenLocalAddress, false).c_str()); - // TODO fallback - } - - epicsSocketDestroy(socket); - - - // set default (auto) address list - _broadcastTransport->setSendAddresses(broadcastAddresses.get()); - - // set broadcast address list - if (!_beaconAddressList.empty()) - { - // if auto is true, add it to specified list - InetAddrVector* appendList = NULL; - if (_autoBeaconAddressList == true) - { - appendList = _broadcastTransport->getSendAddresses(); - } - - auto_ptr list(getSocketAddressList(_beaconAddressList, _broadcastPort, appendList)); - if (list.get() != NULL && list->size() > 0) - { - _broadcastTransport->setSendAddresses(list.get()); - } - } - - - // debug output for broadcast addresses - InetAddrVector* blist = _broadcastTransport->getSendAddresses(); - if (!blist || !blist->size()) - LOG(logLevelWarn, - "No broadcast addresses found or specified!"); - else - for (size_t i = 0; i < blist->size(); i++) - LOG(logLevelDebug, - "Broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); - - // - // set ignore address list - // - if (!_ignoreAddressList.empty()) - { - // we do not care about the port - auto_ptr list(getSocketAddressList(_ignoreAddressList, 0, NULL)); - if (list.get() != NULL && list->size() > 0) - { - _broadcastTransport->setIgnoredAddresses(list.get()); - } - } - - // - // Setup local broadcasting - // - // Each network interface gets its own multicast group on a local interface. - // Multicast address is determined by prefix 224.0.0.124 + NIF index - // - - // TODO configurable local NIF, address - osiSockAddr loAddr; - getLoopbackNIF(loAddr, "", 0); - - osiSockAddr group; - int lastAddr = 128 + ifIndex; - std::ostringstream o; - // TODO configurable prefix and base - o << "224.0.0." << lastAddr; - aToIPAddr(o.str().c_str(), _broadcastPort, &group.ia); - - _broadcastTransport->setMutlicastNIF(loAddr, true); - _broadcastTransport->setLocalMulticastAddress(group); - - if (true) - { - try - { - // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address - _localMulticastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - group, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - _localMulticastTransport->join(group, loAddr); - /* used for sending - _localMulticastTransport->setMutlicastNIF(loAddr, true); - InetAddrVector sendAddressList; - sendAddressList.push_back(group); - _localMulticastTransport->setSendAddresses(&sendAddressList); - */ - - LOG(logLevelDebug, "Local multicast for %s enabled on %s/%s.", - inetAddressToString(listenLocalAddress, false).c_str(), - inetAddressToString(loAddr, false).c_str(), - inetAddressToString(group).c_str()); - } - catch (std::exception& ex) - { - LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); - } - } - else - { - LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); - } - - _broadcastTransport->start(); - if(_broadcastTransport2) - _broadcastTransport2->start(); - if (_localMulticastTransport) - _localMulticastTransport->start(); - } - catch (std::exception& e) - { - THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e); - } - catch (...) - { - THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); - } + } } void ServerContextImpl::run(int32 seconds) @@ -552,34 +579,32 @@ void ServerContextImpl::destroy() void ServerContextImpl::internalDestroy() { // stop responding to search requests - if (_broadcastTransport) - _broadcastTransport->close(); - if (_broadcastTransport2) - _broadcastTransport2->close(); - _broadcastTransport.reset(); - _broadcastTransport2.reset(); + for (BlockingUDPTransportVector::const_iterator iter = _udpTransports.begin(); + iter != _udpTransports.end(); iter++) + (*iter)->close(); + _udpTransports.clear(); - // and close local multicast transport - if (_localMulticastTransport.get()) + // stop emitting beacons + if (_beaconEmitter) { - _localMulticastTransport->close(); - _localMulticastTransport.reset(); + _beaconEmitter->destroy(); + _beaconEmitter.reset(); + } + + // close UDP sent transport + if (_broadcastTransport) + { + _broadcastTransport->close(); + _broadcastTransport.reset(); } // stop accepting connections - if (_acceptor.get()) + if (_acceptor) { _acceptor->destroy(); _acceptor.reset(); } - // stop emitting beacons - if (_beaconEmitter.get()) - { - _beaconEmitter->destroy(); - _beaconEmitter.reset(); - } - // this will also destroy all channels destroyAllTransports(); } @@ -732,12 +757,7 @@ osiSockAddr* ServerContextImpl::getServerInetAddress() BlockingUDPTransport::shared_pointer ServerContextImpl::getBroadcastTransport() { - return _broadcastTransport; -} - -BlockingUDPTransport::shared_pointer ServerContextImpl::getLocalMulticastTransport() -{ - return _localMulticastTransport; + return _broadcastTransport; } ChannelProviderRegistry::shared_pointer ServerContextImpl::getChannelProviderRegistry() diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 4e92ecc..e75f42b 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -145,8 +145,6 @@ public: virtual void newServerDetected(); - BlockingUDPTransport::shared_pointer getLocalMulticastTransport(); - epicsTimeStamp& getStartTime(); @@ -256,13 +254,13 @@ public: */ osiSockAddr* getServerInetAddress(); - /** - * Broadcast transport. - * @return broadcast transport. - */ - BlockingUDPTransport::shared_pointer getBroadcastTransport(); + /** + * Broadcast (UDP send) transport. + * @return broadcast transport. + */ + BlockingUDPTransport::shared_pointer getBroadcastTransport(); - /** + /** * Get channel provider registry implementation used by this instance. * @return channel provider registry used by this instance. */ @@ -310,7 +308,12 @@ private: */ std::string _beaconAddressList; - osiSockAddr _ifaceAddr, _ifaceBCast; + /** + * List of used NIF. + */ + IfaceNodeVector _ifaceList; + + osiSockAddr _ifaceAddr; /** * A space-separated list of address from which to ignore name resolution requests. @@ -349,14 +352,15 @@ private: epics::pvData::Timer::shared_pointer _timer; /** - * Broadcast transport needed for channel searches. + * UDP transports needed to receive channel searches. */ - BlockingUDPTransport::shared_pointer _broadcastTransport, _broadcastTransport2; + typedef std::vector BlockingUDPTransportVector; + BlockingUDPTransportVector _udpTransports; /** - * Local broadcast transport needed for local fan-out. + * UDP socket used to sending. */ - BlockingUDPTransport::shared_pointer _localMulticastTransport; + BlockingUDPTransport::shared_pointer _broadcastTransport; /** * Beacon emitter. diff --git a/src/utils/inetAddressUtil.cpp b/src/utils/inetAddressUtil.cpp index 27cbde5..640f82d 100644 --- a/src/utils/inetAddressUtil.cpp +++ b/src/utils/inetAddressUtil.cpp @@ -248,8 +248,7 @@ static struct ifreq * ifreqNext ( struct ifreq *pifreq ) return ifr; } -int discoverInterfaceIndex - (SOCKET socket, const osiSockAddr *pMatchAddr) +int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr) { static const unsigned nelem = 100; int status; @@ -258,16 +257,7 @@ int discoverInterfaceIndex struct ifreq *pIfreqListEnd; struct ifreq *pifreq; struct ifreq *pnextifreq; - - /* - * check if pMatchAddr is valid - */ - if ( pMatchAddr->sa.sa_family == AF_UNSPEC || - pMatchAddr->sa.sa_family != AF_INET || - pMatchAddr->ia.sin_addr.s_addr == htonl(INADDR_ANY) ) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): invalid pMatchAddr\n"); - return -1; - } + int match; /* * use pool so that we avoid using too much stack space @@ -277,7 +267,7 @@ int discoverInterfaceIndex */ pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) ); if (!pIfreqList) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): no memory to complete request\n"); + errlogPrintf ("discoverInterfaces(): no memory to complete request\n"); return -1; } @@ -285,7 +275,8 @@ int discoverInterfaceIndex ifconf.ifc_req = pIfreqList; status = socket_ioctl (socket, SIOCGIFCONF, &ifconf); if (status < 0 || ifconf.ifc_len == 0) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): unable to fetch network interface configuration\n"); + /*ifDepenDebugPrintf(("discoverInterfaces(): status: 0x08x, ifconf.ifc_len: %d\n", status, ifconf.ifc_len));*/ + errlogPrintf ("discoverInterfaces(): unable to fetch network interface configuration\n"); free (pIfreqList); return -1; } @@ -306,10 +297,16 @@ int discoverInterfaceIndex /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */ memmove(pIfreqList, pifreq, current_ifreqsize); + /*ifDepenDebugPrintf (("discoverInterfaces(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n", + pIfreqList->ifr_name, + ifreq_size(pifreq), + current_ifreqsize));*/ + /* * If its not an internet interface then dont use it */ if ( pIfreqList->ifr_addr.sa_family != AF_INET ) { + /*ifDepenDebugPrintf ( ("discoverInterfaces(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );*/ continue; } @@ -317,38 +314,110 @@ int discoverInterfaceIndex * if it isnt a wildcarded interface then look for * an exact match */ - struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr; - if ( pInetAddr->sin_addr.s_addr == pMatchAddr->ia.sin_addr.s_addr ) { + match = 0; + if ( pMatchAddr && pMatchAddr->sa.sa_family != AF_UNSPEC ) { + if ( pMatchAddr->sa.sa_family != AF_INET ) { + continue; + } + if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) { + struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr; + if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) { + /*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );*/ + continue; + } + else + match = 1; + } + } - unsigned int index = if_nametoindex(pIfreqList->ifr_name); - if ( !index ) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); - free (pIfreqList); - return -1; - } + status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList ); + if ( status ) { + errlogPrintf ("discoverInterfaces(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name); + continue; + } - free (pIfreqList); - return index; + /* + * dont bother with interfaces that have been disabled + */ + if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) { + /*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" was down\n", pIfreqList->ifr_name) );*/ + continue; + } - /* - status = socket_ioctl ( socket, SIOCGIFINDEX, pIfreqList ); - if ( status ) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); - free (pIfreqList); - return -1; - } + /* + * dont use the loop back interface, unless it maches pMatchAddr + */ + if (!match) { + if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) { + /*ifDepenDebugPrintf ( ("discoverInterfaces(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) );*/ + continue; + } + } - free (pIfreqList); - return pIfreqList->ifr_ifindex; - */ + ifaceNode node; + node.ifaceAddr.sa = pIfreqList->ifr_addr; + + /* + * If this is an interface that supports + * broadcast fetch the broadcast address. + * + * Otherwise if this is a point to point + * interface then use the destination address. + * + * Otherwise CA will not query through the + * interface. + */ + if ( pIfreqList->ifr_flags & IFF_BROADCAST ) { + status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList); + if ( status ) { + errlogPrintf ("discoverInterfaces(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name); + continue; + } + node.ifaceBCast.sa = pIfreqList->ifr_broadaddr; + /*ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) );*/ + } +#if defined (IFF_POINTOPOINT) + else if ( pIfreqList->ifr_flags & IFF_POINTOPOINT ) { + status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList); + if ( status ) { + /*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\": pt to pt addr fetch fail\n", pIfreqList->ifr_name) );*/ + continue; + } + node.ifaceBCast.sa = pIfreqList->ifr_dstaddr; + } +#endif + else { + /*ifDepenDebugPrintf ( ( "discoverInterfaces(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );*/ + continue; } - } - /* not found */ - free ( pIfreqList ); - return -1; -} + unsigned int index = if_nametoindex(pIfreqList->ifr_name); + if ( !index ) { + errlogPrintf ("discoverInterfaces(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); + continue; + } + + node.ifaceIndex = index; + + /* + status = socket_ioctl ( socket, SIOCGIFINDEX, pIfreqList ); + if ( status ) { + errlogPrintf ("discoverInterfaces(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); + continue; + } + + node.ifaceIndex = pIfreqList->ifr_ifindex; + */ + + /*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" found\n", pIfreqList->ifr_name) );*/ + + list.push_back(node); + } + + free ( pIfreqList ); + return 0; + } } diff --git a/src/utils/inetAddressUtil.h b/src/utils/inetAddressUtil.h index b46b736..7c83983 100644 --- a/src/utils/inetAddressUtil.h +++ b/src/utils/inetAddressUtil.h @@ -42,6 +42,13 @@ namespace pvAccess { */ epicsShareFunc InetAddrVector* getBroadcastAddresses(SOCKET sock, unsigned short defaultPort); + struct ifaceNode { + int ifaceIndex; + osiSockAddr ifaceAddr, ifaceBCast; + }; + typedef std::vector IfaceNodeVector; + epicsShareFunc int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr = 0); + /** * Returns NIF index for given interface address, or -1 on failure. */ From 9eae48aef4160651ff1e02acc4a970c66d4e3a2f Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Mon, 4 Jan 2016 19:28:17 +0100 Subject: [PATCH 38/51] client: multiple NIF support --- src/remote/abstractResponseHandler.cpp | 4 +- src/remote/blockingUDPTransport.cpp | 7 +- src/remoteClient/clientContextImpl.cpp | 310 +++++++++++++++++-------- src/remoteClient/clientContextImpl.h | 2 - 4 files changed, 215 insertions(+), 108 deletions(-) diff --git a/src/remote/abstractResponseHandler.cpp b/src/remote/abstractResponseHandler.cpp index 4c7c0ec..4c72d66 100644 --- a/src/remote/abstractResponseHandler.cpp +++ b/src/remote/abstractResponseHandler.cpp @@ -22,7 +22,7 @@ namespace epics { namespace pvAccess { void AbstractResponseHandler::handleResponse(osiSockAddr* responseFrom, - Transport::shared_pointer const & /*transport*/, int8 version, int8 command, + Transport::shared_pointer const & transport, int8 version, int8 command, size_t payloadSize, ByteBuffer* payloadBuffer) { if(_debugLevel >= 3) { // TODO make a constant of sth (0 - off, 1 - debug, 2 - more/trace, 3 - messages) char ipAddrStr[48]; @@ -30,7 +30,7 @@ namespace epics { ostringstream prologue; prologue<<"Message [0x"<getRemoteName(); hexDump(prologue.str(), _description, (const int8*)payloadBuffer->getArray(), diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index 8296d56..78ccca7 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -390,8 +390,11 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so (target == inetAddressType_broadcast_multicast && _isSendAddressUnicast[i])) continue; - LOG(logLevelDebug, "Sending to %d bytes to %s.", - buffer->getRemaining(), inetAddressToString((*_sendAddresses)[i]).c_str()); + if (IS_LOGGABLE(logLevelDebug)) + { + LOG(logLevelDebug, "Sending %d bytes to %s.", + buffer->getRemaining(), inetAddressToString((*_sendAddresses)[i]).c_str()); + } int retval = sendto(_channel, buffer->getArray(), buffer->getLimit(), 0, &((*_sendAddresses)[i].sa), diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 83d36bb..88df85c 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -2914,7 +2914,7 @@ namespace epics { // to do the local multicast // - // locally broadcast if unicast (qosCode & 0x80 == 0x80) + // locally broadcast if unicast (qosCode & 0x80 == 0x80) via UDP // if ((qosCode & 0x80) == 0x80) { @@ -2923,11 +2923,8 @@ namespace epics { if (!context) return; - BlockingUDPTransport::shared_pointer bt = - //context->getLocalMulticastTransport(); - std::tr1::dynamic_pointer_cast(context->getSearchTransport()); - - if (bt) + BlockingUDPTransport::shared_pointer bt = dynamic_pointer_cast(transport); + if (bt && bt->hasLocalMulticastAddress()) { // clear unicast flag payloadBuffer->put(startPosition+4, (int8)(qosCode & ~0x80)); @@ -2938,7 +2935,7 @@ namespace epics { payloadBuffer->setPosition(payloadBuffer->getLimit()); // send will call flip() - bt->send(payloadBuffer, context->getLocalBroadcastAddress()); + bt->send(payloadBuffer, bt->getLocalMulticastAddress()); return; } } @@ -4417,11 +4414,6 @@ namespace epics { PVACCESS_REFCOUNT_MONITOR_DESTRUCT(remoteClientContext); }; - virtual const osiSockAddr& getLocalBroadcastAddress() const - { - return m_localBroadcastAddress; - } - private: void loadConfiguration() { @@ -4468,119 +4460,228 @@ namespace epics { */ bool initializeUDPTransport() { - // where to bind (listen) address - osiSockAddr listenLocalAddress; - listenLocalAddress.ia.sin_family = AF_INET; - listenLocalAddress.ia.sin_port = htons(m_broadcastPort); - listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); - - // quary broadcast addresses of all IFs + // query broadcast addresses of all IFs SOCKET socket = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); - if (socket == INVALID_SOCKET) return false; - auto_ptr broadcastAddresses(getBroadcastAddresses(socket, m_broadcastPort)); - - int ifIndex = discoverInterfaceIndex(socket, &listenLocalAddress); - if (ifIndex == -1) + if (socket == INVALID_SOCKET) { - LOG(logLevelWarn, "Unable to find interface index for %s.", inetAddressToString(listenLocalAddress, false).c_str()); - // TODO fallback + LOG(logLevelError, "Failed to initialize broadcast UDP transport."); + return false; + } + + IfaceNodeVector ifaceList; + if (discoverInterfaces(ifaceList, socket, 0) || ifaceList.size() == 0) + { + LOG(logLevelError, "Failed to initialize broadcast UDP transport, no interfaces available."); + return false; } epicsSocketDestroy (socket); - // set broadcast address list - if (!m_addressList.empty()) - { - // if auto is true, add it to specified list - InetAddrVector* appendList = 0; - if (m_autoAddressList) - appendList = broadcastAddresses.get(); - auto_ptr list(getSocketAddressList(m_addressList, m_broadcastPort, appendList)); - if (list.get() && list->size()) { - // delete old list and take ownership of a new one - broadcastAddresses = list; - } - } - - if (!broadcastAddresses.get() || !broadcastAddresses->size()) - LOG(logLevelWarn, - "No broadcast addresses found or specified!"); - else - for (size_t i = 0; i < broadcastAddresses->size(); i++) - LOG(logLevelDebug, - "Broadcast address #%d: %s.", i, inetAddressToString((*broadcastAddresses)[i]).c_str()); - - ClientContextImpl::shared_pointer thisPointer = shared_from_this(); + //ClientContextImpl::shared_pointer thisPointer = shared_from_this(); TransportClient::shared_pointer nullTransportClient; - auto_ptr broadcastConnector(new BlockingUDPConnector(false, true, true)); - m_broadcastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, m_responseHandler, - listenLocalAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - if (!m_broadcastTransport.get()) - return false; - m_broadcastTransport->setSendAddresses(broadcastAddresses.get()); - // undefined address - osiSockAddr undefinedAddress; - undefinedAddress.ia.sin_family = AF_INET; - undefinedAddress.ia.sin_port = htons(0); - undefinedAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + osiSockAddr anyAddress; + anyAddress.ia.sin_family = AF_INET; + anyAddress.ia.sin_port = htons(0); + anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); - auto_ptr searchConnector(new BlockingUDPConnector(false, false, true)); - m_searchTransport = static_pointer_cast(searchConnector->connect( + m_searchTransport = static_pointer_cast(broadcastConnector->connect( nullTransportClient, m_responseHandler, - undefinedAddress, PVA_PROTOCOL_REVISION, + anyAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); if (!m_searchTransport.get()) return false; - m_searchTransport->setSendAddresses(broadcastAddresses.get()); - // TODO do not use searchBroadcast in future - // setup local broadcasting - // TODO configurable local NIF, address - osiSockAddr loAddr; - getLoopbackNIF(loAddr, "", 0); - if (true) + + + + InetAddrVector autoBCastAddr; + for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++) { - try + ifaceNode node = *iter; + + if (node.ifaceBCast.ia.sin_family != AF_UNSPEC) { - int lastAddr = 128 + ifIndex; - std::ostringstream o; - // TODO configurable - o << "224.0.0." << lastAddr; - - //osiSockAddr group; - aToIPAddr(o.str().c_str(), m_broadcastPort, &m_localBroadcastAddress.ia); - m_broadcastTransport->join(m_localBroadcastAddress, loAddr); - - // NOTE: this disables usage of multicast addresses in EPICS_PVA_ADDR_LIST - m_searchTransport->setMutlicastNIF(loAddr, true); - - //InetAddrVector sendAddressList; - //sendAddressList.push_back(group); - //m_searchTransport->setSendAddresses(&sendAddressList); - - LOG(logLevelDebug, "Local multicast enabled on %s using network interface %s.", - inetAddressToString(m_localBroadcastAddress).c_str(), inetAddressToString(loAddr, false).c_str()); + node.ifaceBCast.ia.sin_port = htons(m_broadcastPort); + autoBCastAddr.push_back(node.ifaceBCast); } - catch (std::exception& ex) + } + + // + // set beacon (broadcast) address list + // + + + if (!m_addressList.empty()) + { + // if auto is true, add it to specified list + if (!m_autoAddressList) + autoBCastAddr.clear(); + + auto_ptr list(getSocketAddressList(m_addressList, m_broadcastPort, &autoBCastAddr)); + if (list.get() && list->size()) { - LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); + m_searchTransport->setSendAddresses(list.get()); + } + else // TODO or no fallback at all + { + // fallback + // set default (auto) address list + m_searchTransport->setSendAddresses(&autoBCastAddr); } } else { - LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); + // fallback + // set default (auto) address list + m_searchTransport->setSendAddresses(&autoBCastAddr); } - // become active - m_broadcastTransport->start(); m_searchTransport->start(); + // debug output for broadcast addresses + InetAddrVector* blist = m_searchTransport->getSendAddresses(); + if (!blist || !blist->size()) + LOG(logLevelWarn, + "No broadcast addresses found or specified!"); + else + for (size_t i = 0; i < blist->size(); i++) + LOG(logLevelDebug, + "Broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); + + + // TODO configurable local NIF, address + osiSockAddr loAddr; + getLoopbackNIF(loAddr, "", 0); + + for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++) + { + ifaceNode node = *iter; + + LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s, index %d.", + inetAddressToString(node.ifaceAddr, false).c_str(), + inetAddressToString(node.ifaceBCast, false).c_str(), + node.ifaceIndex); + try + { + // where to bind (listen) address + // TODO opt copy + osiSockAddr listenLocalAddress; + listenLocalAddress.ia.sin_family = AF_INET; + listenLocalAddress.ia.sin_port = htons(m_broadcastPort); + listenLocalAddress.ia.sin_addr.s_addr = node.ifaceAddr.ia.sin_addr.s_addr; + + BlockingUDPTransport::shared_pointer transport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, m_responseHandler, + listenLocalAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + listenLocalAddress = *transport->getRemoteAddress(); + + BlockingUDPTransport::shared_pointer transport2; + + if(node.ifaceBCast.ia.sin_family == AF_UNSPEC || + node.ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { + LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(node.ifaceBCast, false).c_str()); + } + #if !defined(_WIN32) + else + { + /* An oddness of BSD sockets (not winsock) is that binding to + * INADDR_ANY will receive unicast and broadcast, but binding to + * a specific interface address receives only unicast. The trick + * is to bind a second socket to the interface broadcast address, + * which will then receive only broadcasts. + */ + + // TODO opt copy + osiSockAddr bcastAddress; + bcastAddress.ia.sin_family = AF_INET; + bcastAddress.ia.sin_port = htons(m_broadcastPort); + bcastAddress.ia.sin_addr.s_addr = node.ifaceBCast.ia.sin_addr.s_addr; + + transport2 = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, m_responseHandler, + bcastAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + /* The other wrinkle is that nothing should be sent from this second + * socket. So replies are made through the unicast socket. + */ + transport2->setReplyTransport(transport); + } + #endif + + // TODO set ignore list on transport and transport2 + + + // + // Setup local broadcasting + // + // Each network interface gets its own multicast group on a local interface. + // Multicast address is determined by prefix 224.0.0.128 + NIF index + // + + osiSockAddr group; + int lastAddr = 128 + node.ifaceIndex; + std::ostringstream o; + // TODO configurable prefix and base + o << "224.0.0." << lastAddr; + aToIPAddr(o.str().c_str(), m_broadcastPort, &group.ia); + + transport->setMutlicastNIF(loAddr, true); + transport->setLocalMulticastAddress(group); + + BlockingUDPTransport::shared_pointer localMulticastTransport; + + if (true) + { + try + { + // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address + localMulticastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, m_responseHandler, + group, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + localMulticastTransport->join(group, loAddr); + + LOG(logLevelDebug, "Local multicast for %s enabled on %s/%s.", + inetAddressToString(listenLocalAddress, false).c_str(), + inetAddressToString(loAddr, false).c_str(), + inetAddressToString(group).c_str()); + } + catch (std::exception& ex) + { + LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); + } + } + else + { + LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); + } + + transport->start(); + if(transport2) + transport2->start(); + if (localMulticastTransport) + localMulticastTransport->start(); + + m_udpTransports.push_back(transport); + m_udpTransports.push_back(transport2); + m_udpTransports.push_back(localMulticastTransport); + + } + catch (std::exception& e) + { + THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e); + } + catch (...) + { + THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); + } + } + return true; } @@ -4594,8 +4695,14 @@ namespace epics { destroyAllChannels(); // stop UDPs - m_searchTransport->close(); - m_broadcastTransport->close(); + for (BlockingUDPTransportVector::const_iterator iter = m_udpTransports.begin(); + iter != m_udpTransports.end(); iter++) + (*iter)->close(); + m_udpTransports.clear(); + + // stop UDPs + if (m_searchTransport) + m_searchTransport->close(); // wait for all transports to cleanly exit int tries = 40; @@ -4990,9 +5097,10 @@ namespace epics { Timer::shared_pointer m_timer; /** - * Broadcast transport needed to listen for broadcasts. + * UDP transports needed to receive channel searches. */ - BlockingUDPTransport::shared_pointer m_broadcastTransport; + typedef std::vector BlockingUDPTransportVector; + BlockingUDPTransportVector m_udpTransports; /** * UDP transport needed for channel searches. @@ -5105,8 +5213,6 @@ namespace epics { TransportRegistry::transportVector_t m_flushTransports; FlushStrategy m_flushStrategy; - - osiSockAddr m_localBroadcastAddress; }; namespace { diff --git a/src/remoteClient/clientContextImpl.h b/src/remoteClient/clientContextImpl.h index 06e7c02..9f70461 100644 --- a/src/remoteClient/clientContextImpl.h +++ b/src/remoteClient/clientContextImpl.h @@ -129,8 +129,6 @@ namespace epics { virtual void poll() = 0; virtual void destroy() = 0; - - virtual const osiSockAddr& getLocalBroadcastAddress() const = 0; }; epicsShareExtern ChannelProvider::shared_pointer createClientProvider(const Configuration::shared_pointer& conf); From db002e51c8ce5d2da5695358e34dadd727bddeef Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Mon, 4 Jan 2016 20:12:09 +0100 Subject: [PATCH 39/51] TODO cleanup --- src/remoteClient/clientContextImpl.cpp | 3 --- src/server/serverContext.cpp | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 88df85c..64f767a 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -4613,9 +4613,6 @@ namespace epics { } #endif - // TODO set ignore list on transport and transport2 - - // // Setup local broadcasting // diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index f30bd35..95808d9 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -423,9 +423,6 @@ void ServerContextImpl::initializeBroadcastTransport() } #endif - // TODO set ignore list on transport and transport2 - - // // Setup local broadcasting // From 837c10a003c86529b1c1e5d4dc2bcf090a99655c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 20 Dec 2015 09:51:48 -0500 Subject: [PATCH 40/51] fair_queue: struct offsetof() only with POD struct In C++ offsetof() is not specified on non-POD structs (eg. having methods or non-POD members). --- src/utils/fairQueue.h | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/utils/fairQueue.h b/src/utils/fairQueue.h index 976c14a..014f455 100644 --- a/src/utils/fairQueue.h +++ b/src/utils/fairQueue.h @@ -49,7 +49,15 @@ public: typedef std::tr1::shared_ptr value_type; class epicsShareClass entry { - ELLNODE node; + /* In c++, use of ellLib (which implies offsetof()) should be restricted + * to POD structs. So enode_t exists as a POD struct for which offsetof() + * is safe and well defined. enode_t::self is used in place of + * casting via CONTAINER(penode, entry, enode) + */ + struct enode_t { + ELLNODE node; + entry *self; + } enode; unsigned Qcnt; value_type holder; #ifndef NDEBUG @@ -61,16 +69,20 @@ public: entry(const entry&); entry& operator=(const entry&); public: - entry() :node(), Qcnt(0), holder() + entry() :Qcnt(0), holder() #ifndef NDEBUG , owner(NULL) #endif { - node.next = node.previous = NULL; + enode.node.next = enode.node.previous = NULL; + enode.self = this; } ~entry() { // nodes should be removed from the list before deletion - assert(!node.next && !node.previous && !owner); + assert(!enode.node.next && !enode.node.previous); +#ifndef NDEBUG + assert(!owner); +#endif } }; @@ -111,7 +123,7 @@ public: assert(P->owner==NULL); P->owner = this; P->holder = ent; // the list will hold a reference - ellAdd(&list, &P->node); // push_back + ellAdd(&list, &P->enode.node); // push_back } else assert(P->owner==this); } @@ -124,16 +136,18 @@ public: ELLNODE *cur = ellGet(&list); // pop_front if(cur) { - entry *P = CONTAINER(cur, entry, node); + typedef typename entry::enode_t enode_t; + enode_t *PN = CONTAINER(cur, enode_t, node); + entry *P = PN->self; assert(P->owner==this); assert(P->Qcnt>0); if(--P->Qcnt==0) { - P->node.previous = P->node.next = NULL; + PN->node.previous = PN->node.next = NULL; P->owner = NULL; ret.swap(P->holder); } else { - ellAdd(&list, &P->node); // push_back + ellAdd(&list, &P->enode.node); // push_back ret = P->holder; } From 6f85c869bae88fa96cc9de21e53f4cde23e87550 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 5 Jan 2016 13:11:33 -0500 Subject: [PATCH 41/51] configuration: use typeCast instead of epicsParse* Since typeCast wraps epicsParse*, including base 3.14 compatibility. --- src/utils/configuration.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/utils/configuration.cpp b/src/utils/configuration.cpp index a49e2db..d5fa1ac 100644 --- a/src/utils/configuration.cpp +++ b/src/utils/configuration.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -283,32 +284,29 @@ bool Configuration::getPropertyAsBoolean(const std::string &name, const bool def epics::pvData::int32 Configuration::getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue) const { - epicsInt32 ret; - std::string val(getPropertyAsString(name, "")); - - if(epicsParseInt32(val.c_str(), &ret, 0, NULL)) + try{ + return castUnsafe(getPropertyAsString(name, "")); + }catch(std::runtime_error&){ return defaultValue; - return ret; + } } float Configuration::getPropertyAsFloat(const std::string &name, const float defaultValue) const { - float ret; - std::string val(getPropertyAsString(name, "")); - - if(epicsParseFloat(val.c_str(), &ret, NULL)) + try{ + return castUnsafe(getPropertyAsString(name, "")); + }catch(std::runtime_error&){ return defaultValue; - return ret; + } } double Configuration::getPropertyAsDouble(const std::string &name, const double defaultValue) const { - double ret; - std::string val(getPropertyAsString(name, "")); - - if(epicsParseDouble(val.c_str(), &ret, NULL)) + try { + return castUnsafe(getPropertyAsString(name, "")); + }catch(std::runtime_error&){ return defaultValue; - return ret; + } } std::string Configuration::getPropertyAsString(const std::string &name, const std::string &defaultValue) const From 315bd87f8e5e889d08dc869104298b2d5c294d5b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 5 Jan 2016 13:11:38 -0500 Subject: [PATCH 42/51] drop copy of epicsParse* --- src/utils/configuration.cpp | 124 ------------------------------------ 1 file changed, 124 deletions(-) diff --git a/src/utils/configuration.cpp b/src/utils/configuration.cpp index d5fa1ac..713af0b 100644 --- a/src/utils/configuration.cpp +++ b/src/utils/configuration.cpp @@ -4,20 +4,12 @@ * in file LICENSE that is included with this distribution. */ -#include -#include -#include -#include -#include -#include - #include #include #include #include -#include #include #define epicsExportSharedSymbols @@ -34,122 +26,6 @@ namespace pvAccess { using namespace epics::pvData; using namespace std; -#ifndef EPICS_VERSION_INT -#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) -#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL) -#endif - -#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1) -/* integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */ - -#define S_stdlib_noConversion 1 /* No digits to convert */ -#define S_stdlib_extraneous 2 /* Extraneous characters */ -#define S_stdlib_underflow 3 /* Too small to represent */ -#define S_stdlib_overflow 4 /* Too large to represent */ -#define S_stdlib_badBase 5 /* Number base not supported */ - -static int -epicsParseDouble(const char *str, double *to, char **units) -{ - int c; - char *endp; - double value; - - while ((c = *str) && isspace(c)) - ++str; - - errno = 0; - value = epicsStrtod(str, &endp); - - if (endp == str) - return S_stdlib_noConversion; - if (errno == ERANGE) - return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow; - - while ((c = *endp) && isspace(c)) - ++endp; - if (c && !units) - return S_stdlib_extraneous; - - *to = value; - if (units) - *units = endp; - return 0; -} - -static int -epicsParseFloat(const char *str, float *to, char **units) -{ - double value, abs; - int status = epicsParseDouble(str, &value, units); - - if (status) - return status; - - abs = fabs(value); - if (value > 0 && abs <= FLT_MIN) - return S_stdlib_underflow; - // NOTE: 'finite' is deprecated in OS X 10.9, use 'isfinite' instead - if (isfinite(value) && abs >= FLT_MAX) - return S_stdlib_overflow; - - *to = (float)value; - return 0; -} - -static int -epicsParseLong(const char *str, long *to, int base, char **units) -{ - int c; - char *endp; - long value; - - while ((c = *str) && isspace(c)) - ++str; - - errno = 0; - value = strtol(str, &endp, base); - - if (endp == str) - return S_stdlib_noConversion; - if (errno == EINVAL) /* Not universally supported */ - return S_stdlib_badBase; - if (errno == ERANGE) - return S_stdlib_overflow; - - while ((c = *endp) && isspace(c)) - ++endp; - if (c && !units) - return S_stdlib_extraneous; - - *to = value; - if (units) - *units = endp; - return 0; -} - -static int -epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units) -{ - long value; - int status = epicsParseLong(str, &value, base, units); - - if (status) - return status; - -#if (LONG_MAX > 0x7fffffff) - if (value < -0x80000000L || value > 0x7fffffffL) - return S_stdlib_overflow; -#endif - - *to = (epicsInt32)value; - return 0; -} - -#endif - - - Properties::Properties() {} Properties::Properties(const string &fileName) : _fileName(fileName) {} From d7e0365f1eda3a411986367ea822c76f18d60060 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 5 Jan 2016 20:04:48 -0500 Subject: [PATCH 43/51] ServerChannelRequesterImpl::channelStateChange not everything is disconnect --- src/server/responseHandlers.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index ba06d17..02654dd 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -839,8 +839,11 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s } } -void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer const & /*channel*/, const Channel::ConnectionState /*isConnected*/) +void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer const & /*channel*/, const Channel::ConnectionState isConnected) { + if(isConnected==Channel::CONNECTED || isConnected==Channel::NEVER_CONNECTED) + return; + if(Transport::shared_pointer transport = _transport.lock()) { ChannelHostingTransport::shared_pointer casTransport = dynamic_pointer_cast(transport); From 0beb3149a1f868ef90a8ecb64d921c9cd00f4a2e Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 6 Jan 2016 22:35:30 +0100 Subject: [PATCH 44/51] server: channel destroyed before CHANNEL_CREATE message is sent --- src/server/responseHandlers.cpp | 53 ++++++++++++++++----------------- src/server/responseHandlers.h | 1 - 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 02654dd..8a6980f 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -815,7 +815,7 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s LOG(logLevelDebug, "Exception caught when creating channel: %s", _channelName.c_str()); { Lock guard(_mutex); - _status = Status(Status::STATUSTYPE_FATAL, "failed to create channel", e.what()); + _status = Status(Status::STATUSTYPE_FATAL, "failed to create channel", e.what()); } TransportSender::shared_pointer thisSender = shared_from_this(); transport->enqueueSendRequest(thisSender); @@ -828,7 +828,7 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s LOG(logLevelDebug, "Exception caught when creating channel: %s", _channelName.c_str()); { Lock guard(_mutex); - _status = Status(Status::STATUSTYPE_FATAL, "failed to create channel"); + _status = Status(Status::STATUSTYPE_FATAL, "failed to create channel"); } TransportSender::shared_pointer thisSender = shared_from_this(); transport->enqueueSendRequest(thisSender); @@ -905,32 +905,29 @@ void ServerChannelRequesterImpl::send(ByteBuffer* buffer, TransportSendControl* status = _status; } - // error response - if (serverChannel.get() == NULL) - { - createChannelFailedResponse(buffer, control, status); - } - // OK - else if (Transport::shared_pointer transport = _transport.lock()) - { - ServerChannelImpl::shared_pointer serverChannelImpl = dynamic_pointer_cast(serverChannel); - control->startMessage((int8)CMD_CREATE_CHANNEL, 2*sizeof(int32)/sizeof(int8)); - buffer->putInt(serverChannelImpl->getCID()); - buffer->putInt(serverChannelImpl->getSID()); - status.serialize(buffer, control); - } -} - - -void ServerChannelRequesterImpl::createChannelFailedResponse(ByteBuffer* buffer, TransportSendControl* control, const Status& status) -{ - if (Transport::shared_pointer transport = _transport.lock()) - { - control->startMessage((int8)CMD_CREATE_CHANNEL, 2*sizeof(int32)/sizeof(int8)); - buffer->putInt(_cid); - buffer->putInt(-1); - status.serialize(buffer, control); - } + if (Transport::shared_pointer transport = _transport.lock()) + { + // error response + if (!serverChannel) + { + control->startMessage((int8)CMD_CREATE_CHANNEL, 2*sizeof(int32)/sizeof(int8)); + buffer->putInt(_cid); + buffer->putInt(-1); + // error status is expected or channel has been destroyed locally + if (status.isSuccess()) + status = Status(Status::STATUSTYPE_ERROR, "channel has been destroyed"); + status.serialize(buffer, control); + } + // OK + else + { + ServerChannelImpl::shared_pointer serverChannelImpl = dynamic_pointer_cast(serverChannel); + control->startMessage((int8)CMD_CREATE_CHANNEL, 2*sizeof(int32)/sizeof(int8)); + buffer->putInt(serverChannelImpl->getCID()); + buffer->putInt(serverChannelImpl->getSID()); + status.serialize(buffer, control); + } + } } /****************************************************************************************/ diff --git a/src/server/responseHandlers.h b/src/server/responseHandlers.h index fa8eef7..3ceaf71 100644 --- a/src/server/responseHandlers.h +++ b/src/server/responseHandlers.h @@ -263,7 +263,6 @@ namespace pvAccess { ChannelSecuritySession::shared_pointer const & _css; epics::pvData::Status _status; epics::pvData::Mutex _mutex; - void createChannelFailedResponse(epics::pvData::ByteBuffer* buffer, TransportSendControl* control, const epics::pvData::Status& status); }; /****************************************************************************************/ From f0ca714214b83c037819a0c963e6beac4788cf70 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 25 Feb 2016 10:30:09 +0100 Subject: [PATCH 45/51] AUTO_ADDR_LIST - no fallback (as in CA) --- src/remoteClient/clientContextImpl.cpp | 11 ++++++----- src/server/serverContext.cpp | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 64f767a..1f36273 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -4526,16 +4526,17 @@ namespace epics { { m_searchTransport->setSendAddresses(list.get()); } - else // TODO or no fallback at all + /* + else { // fallback // set default (auto) address list m_searchTransport->setSendAddresses(&autoBCastAddr); } + */ } - else + else if (m_autoAddressList) { - // fallback // set default (auto) address list m_searchTransport->setSendAddresses(&autoBCastAddr); } @@ -4545,8 +4546,8 @@ namespace epics { // debug output for broadcast addresses InetAddrVector* blist = m_searchTransport->getSendAddresses(); if (!blist || !blist->size()) - LOG(logLevelWarn, - "No broadcast addresses found or specified!"); + LOG(logLevelError, + "No broadcast addresses found or specified - empty PV search address list!"); else for (size_t i = 0; i < blist->size(); i++) LOG(logLevelDebug, diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 95808d9..4957a74 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -319,16 +319,17 @@ void ServerContextImpl::initializeBroadcastTransport() { _broadcastTransport->setSendAddresses(list.get()); } - else // TODO or no fallback at all + /* + else { // fallback // set default (auto) address list _broadcastTransport->setSendAddresses(&autoBCastAddr); } + */ } - else + else if (_autoBeaconAddressList) { - // fallback // set default (auto) address list _broadcastTransport->setSendAddresses(&autoBCastAddr); } @@ -337,8 +338,8 @@ void ServerContextImpl::initializeBroadcastTransport() // debug output for broadcast addresses InetAddrVector* blist = _broadcastTransport->getSendAddresses(); if (!blist || !blist->size()) - LOG(logLevelWarn, - "No beacon broadcast addresses found or specified!"); + LOG(logLevelError, + "No broadcast addresses found or specified - empty beacon address list!"); else for (size_t i = 0; i < blist->size(); i++) LOG(logLevelDebug, From 7bfe58f1e2c4f3d1efae0ad4a9ae8cbcd4e88cbd Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 25 Feb 2016 10:37:01 +0100 Subject: [PATCH 46/51] UDP transport shutdown fixed --- src/remote/blockingUDPTransport.cpp | 2 +- src/remoteClient/clientContextImpl.cpp | 3 ++- src/server/serverContext.cpp | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index 78ccca7..77e5813 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -154,7 +154,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so // wait for send thread to exit cleanly - if (waitForThreadToComplete) + if (_thread.get() && waitForThreadToComplete) { if (!_shutdownEvent.wait(5.0)) { diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 1f36273..3945250 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -4666,7 +4666,8 @@ namespace epics { localMulticastTransport->start(); m_udpTransports.push_back(transport); - m_udpTransports.push_back(transport2); + if (transport2) + m_udpTransports.push_back(transport2); m_udpTransports.push_back(localMulticastTransport); } diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 4957a74..5ed0ea9 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -476,7 +476,8 @@ void ServerContextImpl::initializeBroadcastTransport() localMulticastTransport->start(); _udpTransports.push_back(transport); - _udpTransports.push_back(transport2); + if(transport2) + _udpTransports.push_back(transport2); _udpTransports.push_back(localMulticastTransport); } From 9be026efc7ddf53c46a92d07e62025274d8e7c3d Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 25 Feb 2016 10:37:44 +0100 Subject: [PATCH 47/51] allow EPICS_PVAS_INTF_ADDR_LIST=127.0.0.1, tests pass now --- src/utils/inetAddressUtil.cpp | 9 +++++++-- testApp/remote/channelAccessIFTest.cpp | 2 -- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/utils/inetAddressUtil.cpp b/src/utils/inetAddressUtil.cpp index 640f82d..6eca68f 100644 --- a/src/utils/inetAddressUtil.cpp +++ b/src/utils/inetAddressUtil.cpp @@ -387,8 +387,13 @@ int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr * } #endif else { - /*ifDepenDebugPrintf ( ( "discoverInterfaces(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );*/ - continue; + if (match) + node.ifaceBCast.sa.sa_family = AF_UNSPEC; + else + { + /*ifDepenDebugPrintf ( ( "discoverInterfaces(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );*/ + continue; + } } diff --git a/testApp/remote/channelAccessIFTest.cpp b/testApp/remote/channelAccessIFTest.cpp index 05a25ba..d1a1082 100755 --- a/testApp/remote/channelAccessIFTest.cpp +++ b/testApp/remote/channelAccessIFTest.cpp @@ -63,7 +63,6 @@ int ChannelAccessIFTest::runAllTest() { .add("EPICS_PVA_ADDR_LIST", "127.0.0.1") .add("EPICS_PVA_AUTO_ADDR_LIST","0") .add("EPICS_PVA_SERVER_PORT", "0") - .add("EPICS_PVA_BROADCAST_PORT", "0") .push_map() .build()); @@ -75,7 +74,6 @@ int ChannelAccessIFTest::runAllTest() { ConfigurationFactory::registerConfiguration("pvAccess-client", ConfigurationBuilder() .push_config(base_config) - .add("EPICS_PVA_BROADCAST_PORT", tstserv->getBroadcastPort()) .push_map() .build()); ScopedClientFactory SCF; From 19031af095003f1d7c2d9e7ee38a5eaaa98ec427 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 1 Mar 2016 08:22:05 +0100 Subject: [PATCH 48/51] added testClientFactory test --- .gitignore | 1 + testApp/remote/Makefile | 2 ++ testApp/remote/testClientFactory.cpp | 15 +++++++++++++++ 3 files changed, 18 insertions(+) create mode 100644 testApp/remote/testClientFactory.cpp diff --git a/.gitignore b/.gitignore index dc47861..dd037da 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ QtC-* *.creator *.files *.includes +*.user diff --git a/testApp/remote/Makefile b/testApp/remote/Makefile index ceea70e..95b8376 100644 --- a/testApp/remote/Makefile +++ b/testApp/remote/Makefile @@ -48,3 +48,5 @@ rpcClientExample_SRCS += rpcClientExample.cpp PROD_HOST += pipelineServiceExample pipelineServiceExample_SRCS += pipelineServiceExample.cpp +TESTPROD_HOST += testClientFactory +testClientFactory_SRCS += testClientFactory.cpp diff --git a/testApp/remote/testClientFactory.cpp b/testApp/remote/testClientFactory.cpp new file mode 100644 index 0000000..c2e085b --- /dev/null +++ b/testApp/remote/testClientFactory.cpp @@ -0,0 +1,15 @@ +#include +#include +#include + +int main() +{ + epics::pvAccess::ClientFactory::start(); + epics::pvAccess::getChannelProviderRegistry()->getProvider("pva"); + epics::pvAccess::ClientFactory::stop(); + + //epicsThreadSleep ( 3.0 ); + //epicsExitCallAtExits(); + + return 0; +} From 1ca3918afa3b2421ec47aeffa8fcfc81074a7d60 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 1 Mar 2016 12:11:25 +0100 Subject: [PATCH 49/51] local multicast reimplemented --- src/remote/blockingUDP.h | 33 ++++++- src/remote/blockingUDPTransport.cpp | 94 ++++++++++++++++--- src/remote/remote.h | 3 +- src/remoteClient/clientContextImpl.cpp | 124 ++++++++++++++----------- src/server/responseHandlers.cpp | 18 +++- src/server/serverContext.cpp | 114 ++++++++++++----------- src/utils/inetAddressUtil.cpp | 19 ---- src/utils/inetAddressUtil.h | 1 - 8 files changed, 260 insertions(+), 146 deletions(-) diff --git a/src/remote/blockingUDP.h b/src/remote/blockingUDP.h index be9745a..e0b2c5b 100644 --- a/src/remote/blockingUDP.h +++ b/src/remote/blockingUDP.h @@ -243,7 +243,7 @@ namespace epics { /** * Set ignore list. - * @param addresses list of ignored addresses. + * @param address list of ignored addresses. */ void setIgnoredAddresses(InetAddrVector* addresses) { if (addresses) @@ -265,6 +265,32 @@ namespace epics { return _ignoredAddresses; } + /** + * Set tapped NIF list. + * @param NIF address list to tap. + */ + void setTappedNIF(InetAddrVector* addresses) { + if (addresses) + { + if (!_tappedNIF) _tappedNIF = new InetAddrVector; + *_tappedNIF = *addresses; + } + else + { + if (_tappedNIF) { delete _tappedNIF; _tappedNIF = 0; } + } + } + + /** + * Get list of tapped NIF addresses. + * @return tapped NIF addresses. + */ + InetAddrVector* getTappedNIF() const { + return _tappedNIF; + } + + bool send(const char* buffer, size_t length, const osiSockAddr& address); + bool send(epics::pvData::ByteBuffer* buffer, const osiSockAddr& address); bool send(epics::pvData::ByteBuffer* buffer, InetAddressType target = inetAddressType_all); @@ -373,6 +399,11 @@ namespace epics { */ InetAddrVector* _ignoredAddresses; + /** + * Tapped NIF addresses. + */ + InetAddrVector* _tappedNIF; + /** * Send address. */ diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index 77e5813..6ce492e 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -38,6 +38,9 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so } #endif +// reserve some space for CMD_ORIGIN_TAG message +#define RECEIVE_BUFFER_PRE_RESERVE PVA_MESSAGE_HEADER_SIZE + 16 + PVACCESS_REFCOUNT_MONITOR_DEFINE(blockingUDPTransport); BlockingUDPTransport::BlockingUDPTransport(bool serverFlag, @@ -50,9 +53,10 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so _bindAddress(bindAddress), _sendAddresses(0), _ignoredAddresses(0), + _tappedNIF(0), _sendToEnabled(false), _localMulticastAddressEnabled(false), - _receiveBuffer(new ByteBuffer(MAX_UDP_RECV)), + _receiveBuffer(new ByteBuffer(MAX_UDP_RECV+RECEIVE_BUFFER_PRE_RESERVE)), _sendBuffer(new ByteBuffer(MAX_UDP_RECV)), _lastMessageStartPosition(0), _clientServerWithEndianFlag( @@ -218,15 +222,20 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so try { + char* recvfrom_buffer_start = (char*)(_receiveBuffer->getArray()+RECEIVE_BUFFER_PRE_RESERVE); + size_t recvfrom_buffer_len =_receiveBuffer->getSize()-RECEIVE_BUFFER_PRE_RESERVE; while(!_closed.get()) { // we poll to prevent blocking indefinitely // data ready to be read _receiveBuffer->clear(); + // reserve some space for CMD_ORIGIN_TAG + _receiveBuffer->setPosition(RECEIVE_BUFFER_PRE_RESERVE); - int bytesRead = recvfrom(_channel, (char*)_receiveBuffer->getArray(), - _receiveBuffer->getRemaining(), 0, (sockaddr*)&fromAddress, + int bytesRead = recvfrom(_channel, + recvfrom_buffer_start, recvfrom_buffer_len, + 0, (sockaddr*)&fromAddress, &addrStructSize); if(likely(bytesRead>0)) { @@ -245,9 +254,8 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so } if(likely(!ignore)) { - _receiveBuffer->setPosition(bytesRead); - - _receiveBuffer->flip(); + _receiveBuffer->setPosition(RECEIVE_BUFFER_PRE_RESERVE); + _receiveBuffer->setLimit(RECEIVE_BUFFER_PRE_RESERVE+bytesRead); try{ processBuffer(replyTo, fromAddress, _receiveBuffer.get()); @@ -322,11 +330,10 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so // second byte version int8 version = receiveBuffer->getByte(); - // only data for UDP int8 flags = receiveBuffer->getByte(); - if (flags < 0) + if (flags & 0x80) { - // 7-bit set + // 7th bit set receiveBuffer->setEndianess(EPICS_ENDIAN_BIG); } else @@ -338,21 +345,80 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so int8 command = receiveBuffer->getByte(); // TODO check this cast (size_t must be 32-bit) size_t payloadSize = receiveBuffer->getInt(); + + // control message check (skip message) + if (flags & 0x01) + continue; + size_t nextRequestPosition = receiveBuffer->getPosition() + payloadSize; // payload size check if(unlikely(nextRequestPosition>receiveBuffer->getLimit())) return false; - // handle - _responseHandler->handleResponse(&fromAddress, replyTransport, - version, command, payloadSize, - _receiveBuffer.get()); + // CMD_ORIGIN_TAG filtering + // NOTE: from design point of view this is not a right place to process application message here + if (unlikely((command == CMD_ORIGIN_TAG) && _tappedNIF)) + { + // 128-bit IPv6 address + osiSockAddr originNIFAddress; + if (decodeAsIPv6Address(receiveBuffer, &originNIFAddress)) + { + originNIFAddress.ia.sin_family = AF_INET; + + /* + LOG(logLevelDebug, "Got CMD_ORIGIN_TAG message form %s tagged as %s.", + inetAddressToString(fromAddress, true).c_str(), + inetAddressToString(originNIFAddress, false).c_str()); + */ + + // filter + if (originNIFAddress.ia.sin_addr.s_addr != htonl(INADDR_ANY)) + { + bool accept = false; + for(size_t i = 0; i < _tappedNIF->size(); i++) + { + if((*_tappedNIF)[i].ia.sin_addr.s_addr == originNIFAddress.ia.sin_addr.s_addr) + { + accept = true; + break; + } + } + + // ignore messages from non-tapped NIFs + if (!accept) + return false; + } + } + } + else + { + // handle + _responseHandler->handleResponse(&fromAddress, replyTransport, + version, command, payloadSize, + _receiveBuffer.get()); + } // set position (e.g. in case handler did not read all) receiveBuffer->setPosition(nextRequestPosition); } - //all ok + // all ok + return true; + } + + bool BlockingUDPTransport::send(const char* buffer, size_t length, const osiSockAddr& address) + { + int retval = sendto(_channel, buffer, + length, 0, &(address.sa), sizeof(sockaddr)); + if(unlikely(retval<0)) + { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + LOG(logLevelDebug, "Socket sendto to %s error: %s.", + inetAddressToString(address).c_str(), errStr); + return false; + } + return true; } diff --git a/src/remote/remote.h b/src/remote/remote.h index acb3a9c..a865ad7 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -114,7 +114,8 @@ namespace epics { CMD_MESSAGE = 18, CMD_MULTIPLE_DATA = 19, CMD_RPC = 20, - CMD_CANCEL_REQUEST = 21 + CMD_CANCEL_REQUEST = 21, + CMD_ORIGIN_TAG = 22 }; enum ControlCommands { diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 3945250..6f64393 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -2926,6 +2926,17 @@ namespace epics { BlockingUDPTransport::shared_pointer bt = dynamic_pointer_cast(transport); if (bt && bt->hasLocalMulticastAddress()) { + // RECEIVE_BUFFER_PRE_RESERVE allows to pre-fix message + size_t newStartPos = (startPosition-PVA_MESSAGE_HEADER_SIZE)-PVA_MESSAGE_HEADER_SIZE-16; + payloadBuffer->setPosition(newStartPos); + + // copy part of a header, and add: command, payloadSize, NIF address + payloadBuffer->put(payloadBuffer->getArray(), startPosition-PVA_MESSAGE_HEADER_SIZE, PVA_MESSAGE_HEADER_SIZE-5); + payloadBuffer->putByte(CMD_ORIGIN_TAG); + payloadBuffer->putInt(16); + // encode this socket bind address + encodeAsIPv6Address(payloadBuffer, bt->getBindAddress()); + // clear unicast flag payloadBuffer->put(startPosition+4, (int8)(qosCode & ~0x80)); @@ -2933,9 +2944,12 @@ namespace epics { payloadBuffer->setPosition(startPosition+8); encodeAsIPv6Address(payloadBuffer, &responseAddress); - payloadBuffer->setPosition(payloadBuffer->getLimit()); // send will call flip() + // set to end of a message + payloadBuffer->setPosition(payloadBuffer->getLimit()); + + bt->send(payloadBuffer->getArray()+newStartPos, payloadBuffer->getPosition()-newStartPos, + bt->getLocalMulticastAddress()); - bt->send(payloadBuffer, bt->getLocalMulticastAddress()); return; } } @@ -4558,14 +4572,59 @@ namespace epics { osiSockAddr loAddr; getLoopbackNIF(loAddr, "", 0); + // + // Setup local multicasting + // + + osiSockAddr group; + // TODO configurable local multicast address + aToIPAddr("224.0.0.128", m_broadcastPort, &group.ia); + + BlockingUDPTransport::shared_pointer localMulticastTransport; + + if (true) + { + try + { + // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address + localMulticastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, m_responseHandler, + group, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + localMulticastTransport->join(group, loAddr); + + if (localMulticastTransport) + { + localMulticastTransport->start(); + m_udpTransports.push_back(localMulticastTransport); + } + + LOG(logLevelDebug, "Local multicast enabled on %s/%s.", + inetAddressToString(loAddr, false).c_str(), + inetAddressToString(group).c_str()); + } + catch (std::exception& ex) + { + LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); + } + } + else + { + LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); + } + + + + + + for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++) { ifaceNode node = *iter; - LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s, index %d.", + LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s.", inetAddressToString(node.ifaceAddr, false).c_str(), - inetAddressToString(node.ifaceBCast, false).c_str(), - node.ifaceIndex); + inetAddressToString(node.ifaceBCast, false).c_str()); try { // where to bind (listen) address @@ -4614,62 +4673,17 @@ namespace epics { } #endif - // - // Setup local broadcasting - // - // Each network interface gets its own multicast group on a local interface. - // Multicast address is determined by prefix 224.0.0.128 + NIF index - // - - osiSockAddr group; - int lastAddr = 128 + node.ifaceIndex; - std::ostringstream o; - // TODO configurable prefix and base - o << "224.0.0." << lastAddr; - aToIPAddr(o.str().c_str(), m_broadcastPort, &group.ia); - transport->setMutlicastNIF(loAddr, true); transport->setLocalMulticastAddress(group); - BlockingUDPTransport::shared_pointer localMulticastTransport; - - if (true) - { - try - { - // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address - localMulticastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, m_responseHandler, - group, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - localMulticastTransport->join(group, loAddr); - - LOG(logLevelDebug, "Local multicast for %s enabled on %s/%s.", - inetAddressToString(listenLocalAddress, false).c_str(), - inetAddressToString(loAddr, false).c_str(), - inetAddressToString(group).c_str()); - } - catch (std::exception& ex) - { - LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); - } - } - else - { - LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); - } - transport->start(); - if(transport2) - transport2->start(); - if (localMulticastTransport) - localMulticastTransport->start(); - m_udpTransports.push_back(transport); - if (transport2) - m_udpTransports.push_back(transport2); - m_udpTransports.push_back(localMulticastTransport); + if (transport2) + { + transport2->start(); + m_udpTransports.push_back(transport2); + } } catch (std::exception& e) { diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 8a6980f..3804cc9 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -266,6 +266,17 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, BlockingUDPTransport::shared_pointer bt = dynamic_pointer_cast(transport); if (bt && bt->hasLocalMulticastAddress()) { + // RECEIVE_BUFFER_PRE_RESERVE allows to pre-fix message + size_t newStartPos = (startPosition-PVA_MESSAGE_HEADER_SIZE)-PVA_MESSAGE_HEADER_SIZE-16; + payloadBuffer->setPosition(newStartPos); + + // copy part of a header, and add: command, payloadSize, NIF address + payloadBuffer->put(payloadBuffer->getArray(), startPosition-PVA_MESSAGE_HEADER_SIZE, PVA_MESSAGE_HEADER_SIZE-5); + payloadBuffer->putByte(CMD_ORIGIN_TAG); + payloadBuffer->putInt(16); + // encode this socket bind address + encodeAsIPv6Address(payloadBuffer, bt->getBindAddress()); + // clear unicast flag payloadBuffer->put(startPosition+4, (int8)(qosCode & ~0x80)); @@ -273,9 +284,12 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, payloadBuffer->setPosition(startPosition+8); encodeAsIPv6Address(payloadBuffer, &responseAddress); - payloadBuffer->setPosition(payloadBuffer->getLimit()); // send will call flip() + // set to end of a message + payloadBuffer->setPosition(payloadBuffer->getLimit()); + + bt->send(payloadBuffer->getArray()+newStartPos, payloadBuffer->getPosition()-newStartPos, + bt->getLocalMulticastAddress()); - bt->send(payloadBuffer, bt->getLocalMulticastAddress()); return; } } diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 5ed0ea9..5dfa0a2 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -361,14 +361,60 @@ void ServerContextImpl::initializeBroadcastTransport() osiSockAddr loAddr; getLoopbackNIF(loAddr, "", 0); + + // + // Setup local multicasting + // + + osiSockAddr group; + // TODO configurable local multicast address + aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); + + BlockingUDPTransport::shared_pointer localMulticastTransport; + + if (true) + { + try + { + // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address + localMulticastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + group, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + localMulticastTransport->join(group, loAddr); + + if (localMulticastTransport) + { + localMulticastTransport->start(); + _udpTransports.push_back(localMulticastTransport); + } + + LOG(logLevelDebug, "Local multicast enabled on %s/%s.", + inetAddressToString(loAddr, false).c_str(), + inetAddressToString(group).c_str()); + } + catch (std::exception& ex) + { + LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); + } + } + else + { + LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); + } + + + + + InetAddrVector tappedNIF; + for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) { ifaceNode node = *iter; - LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s, index %d.", + LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s.", inetAddressToString(node.ifaceAddr, false).c_str(), - inetAddressToString(node.ifaceBCast, false).c_str(), - node.ifaceIndex); + inetAddressToString(node.ifaceBCast, false).c_str()); try { // where to bind (listen) address @@ -387,6 +433,8 @@ void ServerContextImpl::initializeBroadcastTransport() if (ignoreAddressList.get() && ignoreAddressList->size()) transport->setIgnoredAddresses(ignoreAddressList.get()); + tappedNIF.push_back(listenLocalAddress); + BlockingUDPTransport::shared_pointer transport2; @@ -421,65 +469,22 @@ void ServerContextImpl::initializeBroadcastTransport() if (ignoreAddressList.get() && ignoreAddressList->size()) transport2->setIgnoredAddresses(ignoreAddressList.get()); + + tappedNIF.push_back(bcastAddress); } #endif - // - // Setup local broadcasting - // - // Each network interface gets its own multicast group on a local interface. - // Multicast address is determined by prefix 224.0.0.128 + NIF index - // - - osiSockAddr group; - int lastAddr = 128 + node.ifaceIndex; - std::ostringstream o; - // TODO configurable prefix and base - o << "224.0.0." << lastAddr; - aToIPAddr(o.str().c_str(), _broadcastPort, &group.ia); - transport->setMutlicastNIF(loAddr, true); transport->setLocalMulticastAddress(group); - BlockingUDPTransport::shared_pointer localMulticastTransport; - - if (true) - { - try - { - // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address - localMulticastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - group, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - localMulticastTransport->join(group, loAddr); - - LOG(logLevelDebug, "Local multicast for %s enabled on %s/%s.", - inetAddressToString(listenLocalAddress, false).c_str(), - inetAddressToString(loAddr, false).c_str(), - inetAddressToString(group).c_str()); - } - catch (std::exception& ex) - { - LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); - } - } - else - { - LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); - } - transport->start(); - if(transport2) - transport2->start(); - if (localMulticastTransport) - localMulticastTransport->start(); - _udpTransports.push_back(transport); - if(transport2) - _udpTransports.push_back(transport2); - _udpTransports.push_back(localMulticastTransport); + if (transport2) + { + transport2->start(); + _udpTransports.push_back(transport2); + } } catch (std::exception& e) { @@ -490,6 +495,9 @@ void ServerContextImpl::initializeBroadcastTransport() THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); } } + + if (localMulticastTransport) + localMulticastTransport->setTappedNIF(&tappedNIF); } void ServerContextImpl::run(int32 seconds) diff --git a/src/utils/inetAddressUtil.cpp b/src/utils/inetAddressUtil.cpp index 6eca68f..819ca21 100644 --- a/src/utils/inetAddressUtil.cpp +++ b/src/utils/inetAddressUtil.cpp @@ -396,25 +396,6 @@ int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr * } } - - unsigned int index = if_nametoindex(pIfreqList->ifr_name); - if ( !index ) { - errlogPrintf ("discoverInterfaces(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); - continue; - } - - node.ifaceIndex = index; - - /* - status = socket_ioctl ( socket, SIOCGIFINDEX, pIfreqList ); - if ( status ) { - errlogPrintf ("discoverInterfaces(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); - continue; - } - - node.ifaceIndex = pIfreqList->ifr_ifindex; - */ - /*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" found\n", pIfreqList->ifr_name) );*/ list.push_back(node); diff --git a/src/utils/inetAddressUtil.h b/src/utils/inetAddressUtil.h index 7c83983..7972a23 100644 --- a/src/utils/inetAddressUtil.h +++ b/src/utils/inetAddressUtil.h @@ -43,7 +43,6 @@ namespace pvAccess { epicsShareFunc InetAddrVector* getBroadcastAddresses(SOCKET sock, unsigned short defaultPort); struct ifaceNode { - int ifaceIndex; osiSockAddr ifaceAddr, ifaceBCast; }; typedef std::vector IfaceNodeVector; From 87fa71070dc975bbabdfeb9cde419006c4cfea0c Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 2 Mar 2016 11:08:58 +0100 Subject: [PATCH 50/51] automatic broadcast port --- src/server/serverContext.cpp | 246 ++++++++++++------------- testApp/remote/channelAccessIFTest.cpp | 2 + 2 files changed, 122 insertions(+), 126 deletions(-) diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 5dfa0a2..6f4059f 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -165,18 +165,21 @@ void ServerContextImpl::loadConfiguration() _channelProviderNames = config->getPropertyAsString("EPICS_PVA_PROVIDER_NAMES", _channelProviderNames); _channelProviderNames = config->getPropertyAsString("EPICS_PVAS_PROVIDER_NAMES", _channelProviderNames); + // + // introspect network interfaces + // + SOCKET sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); if (!sock) { - THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); + THROW_BASE_EXCEPTION("Failed to create a socket needed to introspect network interfaces."); return; } if (discoverInterfaces(_ifaceList, sock, &_ifaceAddr) || _ifaceList.size() == 0) { - THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport, no interfaces available."); + THROW_BASE_EXCEPTION("Failed to introspect network interfaces or no network interfaces available."); return; } - epicsSocketDestroy(sock); } @@ -191,7 +194,7 @@ void ServerContextImpl::initialize(ChannelProviderRegistry::shared_pointer const Lock guard(_mutex); if (!channelProviderRegistry.get()) { - THROW_BASE_EXCEPTION("non null channelProviderRegistry expected"); + THROW_BASE_EXCEPTION("channelProviderRegistry == NULL"); } if (_state == DESTROYED) @@ -276,136 +279,25 @@ void ServerContextImpl::initializeBroadcastTransport() TransportClient::shared_pointer nullTransportClient; auto_ptr broadcastConnector(new BlockingUDPConnector(true, true, true)); - osiSockAddr anyAddress; - anyAddress.ia.sin_family = AF_INET; - anyAddress.ia.sin_port = htons(0); - anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + // TODO configurable local NIF, address + osiSockAddr loAddr; + getLoopbackNIF(loAddr, "", 0); - _broadcastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - anyAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - - // - // set broadcast (send) addresses - where to send beacons - // - - InetAddrVector autoBCastAddr; - for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) - { - ifaceNode node = *iter; - - if (node.ifaceBCast.ia.sin_family != AF_UNSPEC) - { - node.ifaceBCast.ia.sin_port = htons(_broadcastPort); - autoBCastAddr.push_back(node.ifaceBCast); - } - } - - // - // set beacon (broadcast) address list - // - - - // set broadcast address list - if (!_beaconAddressList.empty()) - { - // if auto is true, add it to specified list - if (!_autoBeaconAddressList) - autoBCastAddr.clear(); - - auto_ptr list(getSocketAddressList(_beaconAddressList, _broadcastPort, &autoBCastAddr)); - if (list.get() && list->size()) - { - _broadcastTransport->setSendAddresses(list.get()); - } - /* - else - { - // fallback - // set default (auto) address list - _broadcastTransport->setSendAddresses(&autoBCastAddr); - } - */ - } - else if (_autoBeaconAddressList) - { - // set default (auto) address list - _broadcastTransport->setSendAddresses(&autoBCastAddr); - } - - - // debug output for broadcast addresses - InetAddrVector* blist = _broadcastTransport->getSendAddresses(); - if (!blist || !blist->size()) - LOG(logLevelError, - "No broadcast addresses found or specified - empty beacon address list!"); - else - for (size_t i = 0; i < blist->size(); i++) - LOG(logLevelDebug, - "Beacon broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); + // TODO configurable local multicast address + osiSockAddr group; + aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); // // set ignore address list // auto_ptr ignoreAddressList; if (!_ignoreAddressList.empty()) - { - // we do not care about the port - ignoreAddressList.reset(getSocketAddressList(_ignoreAddressList, 0, NULL)); - } - - - - // TODO configurable local NIF, address - osiSockAddr loAddr; - getLoopbackNIF(loAddr, "", 0); - + ignoreAddressList.reset(getSocketAddressList(_ignoreAddressList, 0, 0)); // - // Setup local multicasting + // Setup UDP trasport(s) (per interface) // - osiSockAddr group; - // TODO configurable local multicast address - aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); - - BlockingUDPTransport::shared_pointer localMulticastTransport; - - if (true) - { - try - { - // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address - localMulticastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - group, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - localMulticastTransport->join(group, loAddr); - - if (localMulticastTransport) - { - localMulticastTransport->start(); - _udpTransports.push_back(localMulticastTransport); - } - - LOG(logLevelDebug, "Local multicast enabled on %s/%s.", - inetAddressToString(loAddr, false).c_str(), - inetAddressToString(group).c_str()); - } - catch (std::exception& ex) - { - LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); - } - } - else - { - LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); - } - - - - InetAddrVector tappedNIF; for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) @@ -418,7 +310,6 @@ void ServerContextImpl::initializeBroadcastTransport() try { // where to bind (listen) address - // TODO opt copy osiSockAddr listenLocalAddress; listenLocalAddress.ia.sin_family = AF_INET; listenLocalAddress.ia.sin_port = htons(_broadcastPort); @@ -429,6 +320,14 @@ void ServerContextImpl::initializeBroadcastTransport() listenLocalAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); listenLocalAddress = *transport->getRemoteAddress(); + // to allow automatic assignment of broadcast port (for testing) + if (_broadcastPort == 0) + { + _broadcastPort = ntohs(listenLocalAddress.ia.sin_port); + aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); + + LOG(logLevelDebug, "Dynamic broadcast UDP port set to %d.", _broadcastPort); + } if (ignoreAddressList.get() && ignoreAddressList->size()) transport->setIgnoredAddresses(ignoreAddressList.get()); @@ -452,7 +351,6 @@ void ServerContextImpl::initializeBroadcastTransport() * which will then receive only broadcasts. */ - // TODO opt copy osiSockAddr bcastAddress; bcastAddress.ia.sin_family = AF_INET; bcastAddress.ia.sin_port = htons(_broadcastPort); @@ -496,8 +394,104 @@ void ServerContextImpl::initializeBroadcastTransport() } } - if (localMulticastTransport) + + // + // Create UDP transport for sending (to all network interfaces) + // + + osiSockAddr anyAddress; + anyAddress.ia.sin_family = AF_INET; + anyAddress.ia.sin_port = htons(0); + anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + + _broadcastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + anyAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + + // + // compile auto address list - where to send beacons + // + + InetAddrVector autoBCastAddr; + for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) + { + ifaceNode node = *iter; + + if (node.ifaceBCast.ia.sin_family != AF_UNSPEC) + { + node.ifaceBCast.ia.sin_port = htons(_broadcastPort); + autoBCastAddr.push_back(node.ifaceBCast); + } + } + + // + // set beacon (broadcast) address list + // + + if (!_beaconAddressList.empty()) + { + // if auto is true, add it to specified list + if (!_autoBeaconAddressList) + autoBCastAddr.clear(); + + auto_ptr list(getSocketAddressList(_beaconAddressList, _broadcastPort, &autoBCastAddr)); + if (list.get() && list->size()) + { + _broadcastTransport->setSendAddresses(list.get()); + } + /* + else + { + // fallback + // set default (auto) address list + _broadcastTransport->setSendAddresses(&autoBCastAddr); + } + */ + } + else if (_autoBeaconAddressList) + { + // set default (auto) address list + _broadcastTransport->setSendAddresses(&autoBCastAddr); + } + + + // debug output of broadcast addresses + InetAddrVector* blist = _broadcastTransport->getSendAddresses(); + if (!blist || !blist->size()) + LOG(logLevelError, + "No broadcast addresses found or specified - empty beacon address list!"); + else + for (size_t i = 0; i < blist->size(); i++) + LOG(logLevelDebug, + "Beacon broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); + + // + // Setup local multicasting + // + + BlockingUDPTransport::shared_pointer localMulticastTransport; + try + { + // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address + localMulticastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + group, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); localMulticastTransport->setTappedNIF(&tappedNIF); + localMulticastTransport->join(group, loAddr); + localMulticastTransport->start(); + _udpTransports.push_back(localMulticastTransport); + + LOG(logLevelDebug, "Local multicast enabled on %s/%s.", + inetAddressToString(loAddr, false).c_str(), + inetAddressToString(group).c_str()); + } + catch (std::exception& ex) + { + LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); + } + } void ServerContextImpl::run(int32 seconds) diff --git a/testApp/remote/channelAccessIFTest.cpp b/testApp/remote/channelAccessIFTest.cpp index d1a1082..05a25ba 100755 --- a/testApp/remote/channelAccessIFTest.cpp +++ b/testApp/remote/channelAccessIFTest.cpp @@ -63,6 +63,7 @@ int ChannelAccessIFTest::runAllTest() { .add("EPICS_PVA_ADDR_LIST", "127.0.0.1") .add("EPICS_PVA_AUTO_ADDR_LIST","0") .add("EPICS_PVA_SERVER_PORT", "0") + .add("EPICS_PVA_BROADCAST_PORT", "0") .push_map() .build()); @@ -74,6 +75,7 @@ int ChannelAccessIFTest::runAllTest() { ConfigurationFactory::registerConfiguration("pvAccess-client", ConfigurationBuilder() .push_config(base_config) + .add("EPICS_PVA_BROADCAST_PORT", tstserv->getBroadcastPort()) .push_map() .build()); ScopedClientFactory SCF; From 0db4a9a3425fec0ea01c85c2c035f110c60059f3 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 2 Mar 2016 12:37:58 +0100 Subject: [PATCH 51/51] udp transport initialization refactoring (deduplication) --- src/remote/blockingUDP.h | 14 ++ src/remote/blockingUDPTransport.cpp | 304 ++++++++++++++++++++++--- src/remoteClient/clientContextImpl.cpp | 211 +---------------- src/server/serverContext.cpp | 228 +------------------ src/server/serverContext.h | 1 - testApp/remote/channelAccessIFTest.cpp | 1 + 6 files changed, 305 insertions(+), 454 deletions(-) diff --git a/src/remote/blockingUDP.h b/src/remote/blockingUDP.h index e0b2c5b..c7791bf 100644 --- a/src/remote/blockingUDP.h +++ b/src/remote/blockingUDP.h @@ -491,6 +491,20 @@ namespace epics { }; + typedef std::vector BlockingUDPTransportVector; + + epicsShareExtern void initializeUDPTransports( + bool serverFlag, + BlockingUDPTransportVector& udpTransports, + const IfaceNodeVector& ifaceList, + const ResponseHandler::shared_pointer& responseHandler, + BlockingUDPTransport::shared_pointer& sendTransport, + epics::pvData::int32& listenPort, + bool autoAddressList, + const std::string& addressList, + const std::string& ignoreAddressList); + + } } diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index 6ce492e..c2012eb 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -357,36 +357,40 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so // CMD_ORIGIN_TAG filtering // NOTE: from design point of view this is not a right place to process application message here - if (unlikely((command == CMD_ORIGIN_TAG) && _tappedNIF)) + if (unlikely(command == CMD_ORIGIN_TAG)) { - // 128-bit IPv6 address - osiSockAddr originNIFAddress; - if (decodeAsIPv6Address(receiveBuffer, &originNIFAddress)) + // enabled? + if (_tappedNIF) { - originNIFAddress.ia.sin_family = AF_INET; - - /* - LOG(logLevelDebug, "Got CMD_ORIGIN_TAG message form %s tagged as %s.", - inetAddressToString(fromAddress, true).c_str(), - inetAddressToString(originNIFAddress, false).c_str()); - */ - - // filter - if (originNIFAddress.ia.sin_addr.s_addr != htonl(INADDR_ANY)) + // 128-bit IPv6 address + osiSockAddr originNIFAddress; + if (decodeAsIPv6Address(receiveBuffer, &originNIFAddress)) { - bool accept = false; - for(size_t i = 0; i < _tappedNIF->size(); i++) - { - if((*_tappedNIF)[i].ia.sin_addr.s_addr == originNIFAddress.ia.sin_addr.s_addr) - { - accept = true; - break; - } - } + originNIFAddress.ia.sin_family = AF_INET; - // ignore messages from non-tapped NIFs - if (!accept) - return false; + /* + LOG(logLevelDebug, "Got CMD_ORIGIN_TAG message form %s tagged as %s.", + inetAddressToString(fromAddress, true).c_str(), + inetAddressToString(originNIFAddress, false).c_str()); + */ + + // filter + if (originNIFAddress.ia.sin_addr.s_addr != htonl(INADDR_ANY)) + { + bool accept = false; + for(size_t i = 0; i < _tappedNIF->size(); i++) + { + if((*_tappedNIF)[i].ia.sin_addr.s_addr == originNIFAddress.ia.sin_addr.s_addr) + { + accept = true; + break; + } + } + + // ignore messages from non-tapped NIFs + if (!accept) + return false; + } } } } @@ -408,6 +412,12 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so bool BlockingUDPTransport::send(const char* buffer, size_t length, const osiSockAddr& address) { + if (IS_LOGGABLE(logLevelDebug)) + { + LOG(logLevelDebug, "Sending %d bytes to %s.", + length, inetAddressToString(address).c_str()); + } + int retval = sendto(_channel, buffer, length, 0, &(address.sa), sizeof(sockaddr)); if(unlikely(retval<0)) @@ -425,6 +435,13 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so bool BlockingUDPTransport::send(ByteBuffer* buffer, const osiSockAddr& address) { buffer->flip(); + + if (IS_LOGGABLE(logLevelDebug)) + { + LOG(logLevelDebug, "Sending %d bytes to %s.", + buffer->getRemaining(), inetAddressToString(address).c_str()); + } + int retval = sendto(_channel, buffer->getArray(), buffer->getLimit(), 0, &(address.sa), sizeof(sockaddr)); if(unlikely(retval<0)) @@ -552,6 +569,241 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so } + void initializeUDPTransports(bool serverFlag, + BlockingUDPTransportVector& udpTransports, + const IfaceNodeVector& ifaceList, + const ResponseHandler::shared_pointer& responseHandler, + BlockingUDPTransport::shared_pointer& sendTransport, + int32& listenPort, + bool autoAddressList, + const std::string& addressList, + const std::string& ignoreAddressList) + { + TransportClient::shared_pointer nullTransportClient; + auto_ptr connector(new BlockingUDPConnector(serverFlag, true, true)); + + // TODO configurable local NIF, address + osiSockAddr loAddr; + getLoopbackNIF(loAddr, "", 0); + + // TODO configurable local multicast address + std::string mcastAddress("224.0.0.128"); + + osiSockAddr group; + aToIPAddr(mcastAddress.c_str(), listenPort, &group.ia); + + // + // set ignore address list + // + auto_ptr ignoreAddressVector; + if (!ignoreAddressList.empty()) + ignoreAddressVector.reset(getSocketAddressList(ignoreAddressList, 0, 0)); + + // + // Setup UDP trasport(s) (per interface) + // + + InetAddrVector tappedNIF; + + for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++) + { + ifaceNode node = *iter; + + LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s.", + inetAddressToString(node.ifaceAddr, false).c_str(), + inetAddressToString(node.ifaceBCast, false).c_str()); + try + { + // where to bind (listen) address + osiSockAddr listenLocalAddress; + listenLocalAddress.ia.sin_family = AF_INET; + listenLocalAddress.ia.sin_port = htons(listenPort); + listenLocalAddress.ia.sin_addr.s_addr = node.ifaceAddr.ia.sin_addr.s_addr; + + BlockingUDPTransport::shared_pointer transport = static_pointer_cast(connector->connect( + nullTransportClient, responseHandler, + listenLocalAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + listenLocalAddress = *transport->getRemoteAddress(); + // to allow automatic assignment of listen port (for testing) + if (listenPort == 0) + { + listenPort = ntohs(listenLocalAddress.ia.sin_port); + aToIPAddr(mcastAddress.c_str(), listenPort, &group.ia); + + LOG(logLevelDebug, "Dynamic listen UDP port set to %d.", listenPort); + } + + if (ignoreAddressVector.get() && ignoreAddressVector->size()) + transport->setIgnoredAddresses(ignoreAddressVector.get()); + + tappedNIF.push_back(listenLocalAddress); + + + BlockingUDPTransport::shared_pointer transport2; + + if(node.ifaceBCast.ia.sin_family == AF_UNSPEC || + node.ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { + LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(node.ifaceAddr, false).c_str()); + } + #if !defined(_WIN32) + else + { + /* An oddness of BSD sockets (not winsock) is that binding to + * INADDR_ANY will receive unicast and broadcast, but binding to + * a specific interface address receives only unicast. The trick + * is to bind a second socket to the interface broadcast address, + * which will then receive only broadcasts. + */ + + osiSockAddr bcastAddress; + bcastAddress.ia.sin_family = AF_INET; + bcastAddress.ia.sin_port = htons(listenPort); + bcastAddress.ia.sin_addr.s_addr = node.ifaceBCast.ia.sin_addr.s_addr; + + transport2 = static_pointer_cast(connector->connect( + nullTransportClient, responseHandler, + bcastAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + /* The other wrinkle is that nothing should be sent from this second + * socket. So replies are made through the unicast socket. + */ + transport2->setReplyTransport(transport); + + if (ignoreAddressVector.get() && ignoreAddressVector->size()) + transport2->setIgnoredAddresses(ignoreAddressVector.get()); + + tappedNIF.push_back(bcastAddress); + } + #endif + + transport->setMutlicastNIF(loAddr, true); + transport->setLocalMulticastAddress(group); + + transport->start(); + udpTransports.push_back(transport); + + if (transport2) + { + transport2->start(); + udpTransports.push_back(transport2); + } + } + catch (std::exception& e) + { + THROW_BASE_EXCEPTION_CAUSE("Failed to initialize UDP transport.", e); + } + catch (...) + { + THROW_BASE_EXCEPTION("Failed to initialize UDP transport."); + } + } + + + // + // Create UDP transport for sending (to all network interfaces) + // + + osiSockAddr anyAddress; + anyAddress.ia.sin_family = AF_INET; + anyAddress.ia.sin_port = htons(0); + anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + + sendTransport = static_pointer_cast(connector->connect( + nullTransportClient, responseHandler, + anyAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + + // TODO current implementation shares the port (aka beacon and search port) + int32 sendPort = listenPort; + + // + // compile auto address list - where to send packets + // + + InetAddrVector autoBCastAddr; + for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++) + { + ifaceNode node = *iter; + + if (node.ifaceBCast.ia.sin_family != AF_UNSPEC) + { + node.ifaceBCast.ia.sin_port = htons(sendPort); + autoBCastAddr.push_back(node.ifaceBCast); + } + } + + // + // set send address list + // + + if (!addressList.empty()) + { + // if auto is true, add it to specified list + if (!autoAddressList) + autoBCastAddr.clear(); + + auto_ptr list(getSocketAddressList(addressList, sendPort, &autoBCastAddr)); + if (list.get() && list->size()) + { + sendTransport->setSendAddresses(list.get()); + } + /* + else + { + // fallback + // set default (auto) address list + sendTransport->setSendAddresses(&autoBCastAddr); + } + */ + } + else if (autoAddressList) + { + // set default (auto) address list + sendTransport->setSendAddresses(&autoBCastAddr); + } + + + sendTransport->start(); + udpTransports.push_back(sendTransport); + + // debug output of broadcast addresses + InetAddrVector* blist = sendTransport->getSendAddresses(); + if (!blist || !blist->size()) + LOG(logLevelError, + "No broadcast addresses found or specified - empty address list!"); + else + for (size_t i = 0; i < blist->size(); i++) + LOG(logLevelDebug, + "Broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); + + // + // Setup local multicasting + // + + BlockingUDPTransport::shared_pointer localMulticastTransport; + try + { + // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address + localMulticastTransport = static_pointer_cast(connector->connect( + nullTransportClient, responseHandler, + group, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + localMulticastTransport->setTappedNIF(&tappedNIF); + localMulticastTransport->join(group, loAddr); + localMulticastTransport->start(); + udpTransports.push_back(localMulticastTransport); + + LOG(logLevelDebug, "Local multicast enabled on %s/%s.", + inetAddressToString(loAddr, false).c_str(), + inetAddressToString(group).c_str()); + } + catch (std::exception& ex) + { + LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); + } + } + } } diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 6f64393..3db0f6b 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -4478,222 +4478,20 @@ namespace epics { SOCKET socket = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); if (socket == INVALID_SOCKET) { - LOG(logLevelError, "Failed to initialize broadcast UDP transport."); + LOG(logLevelError, "Failed to create a socket to introspect network interfaces."); return false; } IfaceNodeVector ifaceList; if (discoverInterfaces(ifaceList, socket, 0) || ifaceList.size() == 0) { - LOG(logLevelError, "Failed to initialize broadcast UDP transport, no interfaces available."); + LOG(logLevelError, "Failed to introspect interfaces or no network interfaces available."); return false; } - epicsSocketDestroy (socket); - - - //ClientContextImpl::shared_pointer thisPointer = shared_from_this(); - TransportClient::shared_pointer nullTransportClient; - auto_ptr broadcastConnector(new BlockingUDPConnector(false, true, true)); - - osiSockAddr anyAddress; - anyAddress.ia.sin_family = AF_INET; - anyAddress.ia.sin_port = htons(0); - anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); - - m_searchTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, m_responseHandler, - anyAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - if (!m_searchTransport.get()) - return false; - - - - - InetAddrVector autoBCastAddr; - for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++) - { - ifaceNode node = *iter; - - if (node.ifaceBCast.ia.sin_family != AF_UNSPEC) - { - node.ifaceBCast.ia.sin_port = htons(m_broadcastPort); - autoBCastAddr.push_back(node.ifaceBCast); - } - } - - // - // set beacon (broadcast) address list - // - - - if (!m_addressList.empty()) - { - // if auto is true, add it to specified list - if (!m_autoAddressList) - autoBCastAddr.clear(); - - auto_ptr list(getSocketAddressList(m_addressList, m_broadcastPort, &autoBCastAddr)); - if (list.get() && list->size()) - { - m_searchTransport->setSendAddresses(list.get()); - } - /* - else - { - // fallback - // set default (auto) address list - m_searchTransport->setSendAddresses(&autoBCastAddr); - } - */ - } - else if (m_autoAddressList) - { - // set default (auto) address list - m_searchTransport->setSendAddresses(&autoBCastAddr); - } - - m_searchTransport->start(); - - // debug output for broadcast addresses - InetAddrVector* blist = m_searchTransport->getSendAddresses(); - if (!blist || !blist->size()) - LOG(logLevelError, - "No broadcast addresses found or specified - empty PV search address list!"); - else - for (size_t i = 0; i < blist->size(); i++) - LOG(logLevelDebug, - "Broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); - - - // TODO configurable local NIF, address - osiSockAddr loAddr; - getLoopbackNIF(loAddr, "", 0); - - // - // Setup local multicasting - // - - osiSockAddr group; - // TODO configurable local multicast address - aToIPAddr("224.0.0.128", m_broadcastPort, &group.ia); - - BlockingUDPTransport::shared_pointer localMulticastTransport; - - if (true) - { - try - { - // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address - localMulticastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, m_responseHandler, - group, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - localMulticastTransport->join(group, loAddr); - - if (localMulticastTransport) - { - localMulticastTransport->start(); - m_udpTransports.push_back(localMulticastTransport); - } - - LOG(logLevelDebug, "Local multicast enabled on %s/%s.", - inetAddressToString(loAddr, false).c_str(), - inetAddressToString(group).c_str()); - } - catch (std::exception& ex) - { - LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); - } - } - else - { - LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); - } - - - - - - - for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++) - { - ifaceNode node = *iter; - - LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s.", - inetAddressToString(node.ifaceAddr, false).c_str(), - inetAddressToString(node.ifaceBCast, false).c_str()); - try - { - // where to bind (listen) address - // TODO opt copy - osiSockAddr listenLocalAddress; - listenLocalAddress.ia.sin_family = AF_INET; - listenLocalAddress.ia.sin_port = htons(m_broadcastPort); - listenLocalAddress.ia.sin_addr.s_addr = node.ifaceAddr.ia.sin_addr.s_addr; - - BlockingUDPTransport::shared_pointer transport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, m_responseHandler, - listenLocalAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - listenLocalAddress = *transport->getRemoteAddress(); - - BlockingUDPTransport::shared_pointer transport2; - - if(node.ifaceBCast.ia.sin_family == AF_UNSPEC || - node.ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { - LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(node.ifaceBCast, false).c_str()); - } - #if !defined(_WIN32) - else - { - /* An oddness of BSD sockets (not winsock) is that binding to - * INADDR_ANY will receive unicast and broadcast, but binding to - * a specific interface address receives only unicast. The trick - * is to bind a second socket to the interface broadcast address, - * which will then receive only broadcasts. - */ - - // TODO opt copy - osiSockAddr bcastAddress; - bcastAddress.ia.sin_family = AF_INET; - bcastAddress.ia.sin_port = htons(m_broadcastPort); - bcastAddress.ia.sin_addr.s_addr = node.ifaceBCast.ia.sin_addr.s_addr; - - transport2 = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, m_responseHandler, - bcastAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - /* The other wrinkle is that nothing should be sent from this second - * socket. So replies are made through the unicast socket. - */ - transport2->setReplyTransport(transport); - } - #endif - - transport->setMutlicastNIF(loAddr, true); - transport->setLocalMulticastAddress(group); - - transport->start(); - m_udpTransports.push_back(transport); - - if (transport2) - { - transport2->start(); - m_udpTransports.push_back(transport2); - } - } - catch (std::exception& e) - { - THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e); - } - catch (...) - { - THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); - } - } + initializeUDPTransports(false, m_udpTransports, ifaceList, m_responseHandler, m_searchTransport, + m_broadcastPort, m_autoAddressList, m_addressList, std::string()); return true; } @@ -5112,7 +4910,6 @@ namespace epics { /** * UDP transports needed to receive channel searches. */ - typedef std::vector BlockingUDPTransportVector; BlockingUDPTransportVector m_udpTransports; /** diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 6f4059f..1698f0a 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -172,13 +172,15 @@ void ServerContextImpl::loadConfiguration() SOCKET sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); if (!sock) { THROW_BASE_EXCEPTION("Failed to create a socket needed to introspect network interfaces."); - return; } - if (discoverInterfaces(_ifaceList, sock, &_ifaceAddr) || _ifaceList.size() == 0) + if (discoverInterfaces(_ifaceList, sock, &_ifaceAddr)) { - THROW_BASE_EXCEPTION("Failed to introspect network interfaces or no network interfaces available."); - return; + THROW_BASE_EXCEPTION("Failed to introspect network interfaces."); + } + else if (_ifaceList.size() == 0) + { + THROW_BASE_EXCEPTION("No (specified) network interface(s) available."); } epicsSocketDestroy(sock); } @@ -276,222 +278,8 @@ void ServerContextImpl::internalInitialize() void ServerContextImpl::initializeBroadcastTransport() { - TransportClient::shared_pointer nullTransportClient; - auto_ptr broadcastConnector(new BlockingUDPConnector(true, true, true)); - - // TODO configurable local NIF, address - osiSockAddr loAddr; - getLoopbackNIF(loAddr, "", 0); - - // TODO configurable local multicast address - osiSockAddr group; - aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); - - // - // set ignore address list - // - auto_ptr ignoreAddressList; - if (!_ignoreAddressList.empty()) - ignoreAddressList.reset(getSocketAddressList(_ignoreAddressList, 0, 0)); - - // - // Setup UDP trasport(s) (per interface) - // - - InetAddrVector tappedNIF; - - for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) - { - ifaceNode node = *iter; - - LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s.", - inetAddressToString(node.ifaceAddr, false).c_str(), - inetAddressToString(node.ifaceBCast, false).c_str()); - try - { - // where to bind (listen) address - osiSockAddr listenLocalAddress; - listenLocalAddress.ia.sin_family = AF_INET; - listenLocalAddress.ia.sin_port = htons(_broadcastPort); - listenLocalAddress.ia.sin_addr.s_addr = node.ifaceAddr.ia.sin_addr.s_addr; - - BlockingUDPTransport::shared_pointer transport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - listenLocalAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - listenLocalAddress = *transport->getRemoteAddress(); - // to allow automatic assignment of broadcast port (for testing) - if (_broadcastPort == 0) - { - _broadcastPort = ntohs(listenLocalAddress.ia.sin_port); - aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); - - LOG(logLevelDebug, "Dynamic broadcast UDP port set to %d.", _broadcastPort); - } - - if (ignoreAddressList.get() && ignoreAddressList->size()) - transport->setIgnoredAddresses(ignoreAddressList.get()); - - tappedNIF.push_back(listenLocalAddress); - - - BlockingUDPTransport::shared_pointer transport2; - - if(node.ifaceBCast.ia.sin_family == AF_UNSPEC || - node.ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { - LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(node.ifaceBCast, false).c_str()); - } - #if !defined(_WIN32) - else - { - /* An oddness of BSD sockets (not winsock) is that binding to - * INADDR_ANY will receive unicast and broadcast, but binding to - * a specific interface address receives only unicast. The trick - * is to bind a second socket to the interface broadcast address, - * which will then receive only broadcasts. - */ - - osiSockAddr bcastAddress; - bcastAddress.ia.sin_family = AF_INET; - bcastAddress.ia.sin_port = htons(_broadcastPort); - bcastAddress.ia.sin_addr.s_addr = node.ifaceBCast.ia.sin_addr.s_addr; - - transport2 = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - bcastAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - /* The other wrinkle is that nothing should be sent from this second - * socket. So replies are made through the unicast socket. - */ - transport2->setReplyTransport(transport); - - if (ignoreAddressList.get() && ignoreAddressList->size()) - transport2->setIgnoredAddresses(ignoreAddressList.get()); - - tappedNIF.push_back(bcastAddress); - } - #endif - - transport->setMutlicastNIF(loAddr, true); - transport->setLocalMulticastAddress(group); - - transport->start(); - _udpTransports.push_back(transport); - - if (transport2) - { - transport2->start(); - _udpTransports.push_back(transport2); - } - } - catch (std::exception& e) - { - THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e); - } - catch (...) - { - THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); - } - } - - - // - // Create UDP transport for sending (to all network interfaces) - // - - osiSockAddr anyAddress; - anyAddress.ia.sin_family = AF_INET; - anyAddress.ia.sin_port = htons(0); - anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); - - _broadcastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - anyAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - - // - // compile auto address list - where to send beacons - // - - InetAddrVector autoBCastAddr; - for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) - { - ifaceNode node = *iter; - - if (node.ifaceBCast.ia.sin_family != AF_UNSPEC) - { - node.ifaceBCast.ia.sin_port = htons(_broadcastPort); - autoBCastAddr.push_back(node.ifaceBCast); - } - } - - // - // set beacon (broadcast) address list - // - - if (!_beaconAddressList.empty()) - { - // if auto is true, add it to specified list - if (!_autoBeaconAddressList) - autoBCastAddr.clear(); - - auto_ptr list(getSocketAddressList(_beaconAddressList, _broadcastPort, &autoBCastAddr)); - if (list.get() && list->size()) - { - _broadcastTransport->setSendAddresses(list.get()); - } - /* - else - { - // fallback - // set default (auto) address list - _broadcastTransport->setSendAddresses(&autoBCastAddr); - } - */ - } - else if (_autoBeaconAddressList) - { - // set default (auto) address list - _broadcastTransport->setSendAddresses(&autoBCastAddr); - } - - - // debug output of broadcast addresses - InetAddrVector* blist = _broadcastTransport->getSendAddresses(); - if (!blist || !blist->size()) - LOG(logLevelError, - "No broadcast addresses found or specified - empty beacon address list!"); - else - for (size_t i = 0; i < blist->size(); i++) - LOG(logLevelDebug, - "Beacon broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); - - // - // Setup local multicasting - // - - BlockingUDPTransport::shared_pointer localMulticastTransport; - try - { - // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address - localMulticastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - group, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - localMulticastTransport->setTappedNIF(&tappedNIF); - localMulticastTransport->join(group, loAddr); - localMulticastTransport->start(); - _udpTransports.push_back(localMulticastTransport); - - LOG(logLevelDebug, "Local multicast enabled on %s/%s.", - inetAddressToString(loAddr, false).c_str(), - inetAddressToString(group).c_str()); - } - catch (std::exception& ex) - { - LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); - } - + initializeUDPTransports(true, _udpTransports, _ifaceList, _responseHandler, _broadcastTransport, + _broadcastPort, _autoBeaconAddressList, _beaconAddressList, _ignoreAddressList); } void ServerContextImpl::run(int32 seconds) diff --git a/src/server/serverContext.h b/src/server/serverContext.h index e75f42b..675d0ab 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -354,7 +354,6 @@ private: /** * UDP transports needed to receive channel searches. */ - typedef std::vector BlockingUDPTransportVector; BlockingUDPTransportVector _udpTransports; /** diff --git a/testApp/remote/channelAccessIFTest.cpp b/testApp/remote/channelAccessIFTest.cpp index 05a25ba..5e480b8 100755 --- a/testApp/remote/channelAccessIFTest.cpp +++ b/testApp/remote/channelAccessIFTest.cpp @@ -59,6 +59,7 @@ int ChannelAccessIFTest::runAllTest() { testPlan(153+EXTRA_STRESS_TESTS); Configuration::shared_pointer base_config(ConfigurationBuilder() + //.add("EPICS_PVA_DEBUG", "3") .add("EPICS_PVAS_INTF_ADDR_LIST", "127.0.0.1") .add("EPICS_PVA_ADDR_LIST", "127.0.0.1") .add("EPICS_PVA_AUTO_ADDR_LIST","0")