diff --git a/.hgtags b/.hgtags index 0b4aa96..2a2a76e 100644 --- a/.hgtags +++ b/.hgtags @@ -13,3 +13,8 @@ c17653ce8b54de97d9a1a89ff9fe8604a7901ceb 3.0.2 d720da04d44ae6ed4a9d548717f453a0fbaa9e56 3.0.4 90a0844d7098ae1b06429ae6f931e21f8af6d48f before_merge_changesAfter3_0_2 1d5059b8308b3960ddf750883fa0ed482ca11c34 3.1.0 +4ef1b1dbbb6104c24960bff5c650f702e4b494dc 4.0.0 +4ef1b1dbbb6104c24960bff5c650f702e4b494dc 4.0.0 +ceca448e7c62c23388a0c866c905c7080633a875 4.0.0 +ceca448e7c62c23388a0c866c905c7080633a875 4.0.0 +cf6fc9696904fd1735523a70a4f59b5ad6a3f2d5 4.0.0 diff --git a/TODO b/TODO index 8801c31..5c5ba4a 100644 --- a/TODO +++ b/TODO @@ -4,8 +4,8 @@ // should be called without any lock hold // TODO in Java as in C++ ? reportChannelStateChange(); -- searching of channel with server address specified +- improve searching of channel with server address specified - void transportUnresponsive() { not implemented (also in Java) -- complete authNZ -- improve RPCClient (as it is in Java) +- complete authNZ (callback on right change) +- request event on disconnect/destroy, etc.? diff --git a/pvAccessCPP.files b/pvAccessCPP.files index 19574db..096a09e 100644 --- a/pvAccessCPP.files +++ b/pvAccessCPP.files @@ -542,3 +542,4 @@ testApp/utils/testAtomicBoolean.cpp testApp/utils/testHexDump.cpp testApp/utils/testInetAddressUtils.cpp testApp/utils/transportRegistryTest.cpp +pvtoolsSrc/pvlist.cpp diff --git a/pvtoolsSrc/Makefile b/pvtoolsSrc/Makefile index 400de95..055d918 100644 --- a/pvtoolsSrc/Makefile +++ b/pvtoolsSrc/Makefile @@ -14,6 +14,10 @@ PROD_HOST += pvinfo pvinfo_SRCS += pvinfo.cpp pvinfo_LIBS += pvAccess pvData pvMB Com +PROD_HOST += pvlist +pvlist_SRCS += pvlist.cpp +pvlist_LIBS += pvAccess pvData pvMB Com + PROD_HOST += eget eget_SRCS += eget.cpp eget_LIBS += pvAccess pvData pvMB ca Com diff --git a/pvtoolsSrc/pvlist.cpp b/pvtoolsSrc/pvlist.cpp new file mode 100644 index 0000000..7d8d550 --- /dev/null +++ b/pvtoolsSrc/pvlist.cpp @@ -0,0 +1,522 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::tr1; + +using namespace epics::pvData; +using namespace epics::pvAccess; + +/// Byte to hexchar mapping. +static const char lookup[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +/// Get hex representation of byte. +string toHex(int8* ba, size_t len) { + string sb; + + for (size_t i = 0; i < len; i++) + { + int8 b = ba[i]; + + int upper = (b>>4)&0x0F; + sb += lookup[upper]; + + int lower = b&0x0F; + sb += lookup[lower]; + } + + return sb; +} + + +static string emptyString; + +std::size_t readSize(ByteBuffer* buffer) { + int8 b = buffer->getByte(); + if(b==-1) + return -1; + else if(b==-2) { + int32 s = buffer->getInt(); + if(s<0) THROW_BASE_EXCEPTION("negative size"); + return s; + } + else + return (std::size_t)(b<0 ? b+256 : b); +} + +string deserializeString(ByteBuffer* buffer) { + + std::size_t size = /*SerializeHelper::*/readSize(buffer); + if(size!=(size_t)-1) // TODO null strings check, to be removed in the future + { + // entire string is in buffer, simply create a string out of it (copy) + std::size_t pos = buffer->getPosition(); + string str(buffer->getArray()+pos, size); + buffer->setPosition(pos+size); + return str; + } + else + return emptyString; +} + +struct ServerEntry { + string guid; + string protocol; + vector addresses; + int8 version; +}; + + +typedef map ServerMap; +static ServerMap serverMap; + +void processSearchResponse(osiSockAddr const & responseFrom, ByteBuffer & receiveBuffer) +{ + // first byte is PVA_MAGIC + int8 magic = receiveBuffer.getByte(); + if(magic != PVA_MAGIC) + return; + + // second byte version + int8 version = receiveBuffer.getByte(); + + // only data for UDP + int8 flags = receiveBuffer.getByte(); + if (flags < 0) + { + // 7-bit set + receiveBuffer.setEndianess(EPICS_ENDIAN_BIG); + } + else + { + receiveBuffer.setEndianess(EPICS_ENDIAN_LITTLE); + } + + // command ID and paylaod + int8 command = receiveBuffer.getByte(); + if (command != (int8)0x04) + return; + + size_t payloadSize = receiveBuffer.getInt(); + if (payloadSize < (12+4+16+2)) + return; + + + GUID guid; + receiveBuffer.get(guid.value, 0, sizeof(guid.value)); + + /*int32 searchSequenceId = */receiveBuffer.getInt(); + + osiSockAddr serverAddress; + serverAddress.ia.sin_family = AF_INET; + + // 128-bit IPv6 address + if (!decodeAsIPv6Address(&receiveBuffer, &serverAddress)) return; + + // accept given address if explicitly specified by sender + if (serverAddress.ia.sin_addr.s_addr == INADDR_ANY) + serverAddress.ia.sin_addr = responseFrom.ia.sin_addr; + + serverAddress.ia.sin_port = htons(receiveBuffer.getShort()); + + string protocol = /*SerializeHelper::*/deserializeString(&receiveBuffer); + + /*bool found =*/ receiveBuffer.getByte(); // != 0; + + + string guidString = toHex((int8*)guid.value, sizeof(guid.value)); + + ServerMap::iterator iter = serverMap.find(guidString); + if (iter != serverMap.end()) + { + bool found = false; + vector& vec = iter->second.addresses; + for (vector::const_iterator ai = vec.begin(); + ai != vec.end(); + ai++) + if (sockAddrAreIdentical(&(*ai), &serverAddress)) + { + found = true; + break; + } + + if (!found) + vec.push_back(serverAddress); + } + else + { + ServerEntry serverEntry; + serverEntry.guid = guidString; + serverEntry.protocol = protocol; + serverEntry.addresses.push_back(serverAddress); + serverEntry.version = version; + + serverMap[guidString] = serverEntry; + } + + return; +} + +bool discoverServers(double timeOut) +{ + osiSockAttach(); + + SOCKET socket = epicsSocketCreate(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (socket == INVALID_SOCKET) + { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + fprintf(stderr, "Failed to create a socket: %s\n", errStr); + return false; + } + + // + // read config + // + + Configuration::shared_pointer configuration(new SystemConfigurationImpl()); + + string addressList = configuration->getPropertyAsString("EPICS_PVA_ADDR_LIST", ""); + bool autoAddressList = configuration->getPropertyAsBoolean("EPICS_PVA_AUTO_ADDR_LIST", true); + int broadcastPort = configuration->getPropertyAsInteger("EPICS_PVA_BROADCAST_PORT", PVA_BROADCAST_PORT); + + // quary broadcast addresses of all IFs + auto_ptr broadcastAddresses(getBroadcastAddresses(socket, broadcastPort)); + + // set broadcast address list + if (!addressList.empty()) + { + // if auto is true, add it to specified list + InetAddrVector* appendList = 0; + if (autoAddressList) + appendList = broadcastAddresses.get(); + + auto_ptr list(getSocketAddressList(addressList, broadcastPort, appendList)); + if (list.get() && list->size()) { + // delete old list and take ownership of a new one + broadcastAddresses = list; + } + } + + for (size_t i = 0; broadcastAddresses.get() && i < broadcastAddresses->size(); i++) + LOG(logLevelDebug, + "Broadcast address #%d: %s.", i, inetAddressToString((*broadcastAddresses)[i]).c_str()); + + // --- + + int optval = 1; + int status = ::setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval)); + if (status) + { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + fprintf(stderr, "Error setting SO_BROADCAST: %s\n", errStr); + epicsSocketDestroy (socket); + return false; + } + + osiSockAddr bindAddr; + bindAddr.ia.sin_family = AF_INET; + bindAddr.ia.sin_port = htons(0); + bindAddr.ia.sin_addr.s_addr = htonl(INADDR_ANY); + + status = ::bind(socket, (sockaddr*)&(bindAddr.sa), sizeof(sockaddr)); + if (status) + { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + fprintf(stderr, "Failed to bind: %s\n", errStr); + epicsSocketDestroy(socket); + return false; + } + + osiSockAddr responseAddress; + osiSocklen_t sockLen = sizeof(sockaddr); + // read the actual socket info + status = ::getsockname(socket, &responseAddress.sa, &sockLen); + if (status) { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + fprintf(stderr, "Failed to get local socket address: %s.", errStr); + return false; + } + + char buffer[1024]; + ByteBuffer sendBuffer(buffer, sizeof(buffer)/sizeof(char)); + + sendBuffer.putByte(PVA_MAGIC); + sendBuffer.putByte(PVA_VERSION); + sendBuffer.putByte((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00); // data + 7-bit endianess + sendBuffer.putByte((int8_t)3); // search + sendBuffer.putInt(4+1+3+16+2+1); // "zero" payload + + sendBuffer.putInt(0); // sequenceId + sendBuffer.putByte((int8_t)0x81); // reply required // TODO unicast vs multicast; for now we mark ourselves as unicast + sendBuffer.putByte((int8_t)0); // reserved + sendBuffer.putShort((int16_t)0); // reserved + + // NOTE: is it possible (very likely) that address is any local address ::ffff:0.0.0.0 + encodeAsIPv6Address(&sendBuffer, &responseAddress); + sendBuffer.putShort((int16_t)ntohs(responseAddress.ia.sin_port)); + + sendBuffer.putByte((int8_t)0x00); // no restriction on protocol + sendBuffer.putShort((int16_t)0); // count + + bool oneOK = false; + for (size_t i = 0; i < broadcastAddresses->size(); i++) + { + // send the packet + status = ::sendto(socket, sendBuffer.getArray(), sendBuffer.getPosition(), 0, + &((*broadcastAddresses)[i].sa), sizeof(sockaddr)); + if (status < 0) + { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + fprintf(stderr, "Send error: %s\n", errStr); + } + else + oneOK = true; + } + + if (!oneOK) + return false; + + + // set timeout in case message is not sent + struct timeval timeout; + memset(&timeout, 0, sizeof(struct timeval)); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + status = ::setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, + (char*)&timeout, sizeof(timeout)); + if (status) + { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + fprintf(stderr, "Error setting SO_RCVTIMEO: %s\n", errStr); + return false; + } + + char rxbuff[1024]; + ByteBuffer receiveBuffer(rxbuff, sizeof(rxbuff)/sizeof(char)); + + osiSockAddr fromAddress; + osiSocklen_t addrStructSize = sizeof(sockaddr); + + while (true) + { + receiveBuffer.clear(); + + // receive packet from socket + int bytesRead = ::recvfrom(socket, (char*)receiveBuffer.getArray(), + receiveBuffer.getRemaining(), 0, + (sockaddr*)&fromAddress, &addrStructSize); + if (bytesRead > 0) + { + receiveBuffer.setPosition(bytesRead); + receiveBuffer.flip(); + + processSearchResponse(fromAddress, receiveBuffer); + + } + else if (status <= 0) + { + if (status == -1) + { + int socketError = SOCKERRNO; + + // interrupted or timeout + if (socketError == SOCK_EINTR || + socketError == EAGAIN || // no alias in libCom + // windows times out with this + //socketError == SOCK_ETIMEDOUT || + socketError == SOCK_EWOULDBLOCK) + continue; + + if (socketError == SOCK_ECONNREFUSED || // avoid spurious ECONNREFUSED in Linux + socketError == SOCK_ECONNRESET) // or ECONNRESET in Windows + continue; + + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + fprintf(stderr, "Socket recv error: %s\n", errStr); + } + break; + } + + } + + + for (ServerMap::const_iterator iter = serverMap.begin(); + iter != serverMap.end(); + iter++) + { + const ServerEntry& entry = iter->second; + + cout << "GUID 0x" << entry.guid << ", version " << entry.version << ": " + << entry.protocol << "@["; + + size_t count = entry.addresses.size(); + for (size_t i = 0; i < count; i++) + { + cout << inetAddressToString(entry.addresses[i]); + if (i < (count-1)) + cout << ", "; + } + cout << ']' << endl; + } + + // TODO shutdown sockets? + // TODO this resouce is not released on failure + epicsSocketDestroy(socket); + + return true; +} + + +#define DEFAULT_TIMEOUT 3.0 + +void usage (void) +{ + fprintf (stderr, "\nUsage: pvlist [options] ...\n\n" + " -h: Help: Print this message\n" + "options:\n" + " -w : Wait time, specifies timeout, default is %f second(s)\n" + " -q: Quiet mode, print only error messages\n" + " -d: Enable debug output\n" + " -F : Use as an alternate output field separator\n" +// " -f : Use as an input that provides a list input parameters(s) to be read, use '-' for stdin\n" + "\nexample: pvinfo ...\n\n" + , DEFAULT_TIMEOUT); +} + + +/*+************************************************************************** + * + * Function: main + * + * Description: pvlist main() + * Evaluate command line options, ... + * + * Arg(s) In: [options] []... + * + * Arg(s) Out: none + * + * Return(s): Standard return code (0=success, 1=error) + * + **************************************************************************-*/ + +int main (int argc, char *argv[]) +{ + int opt; /* getopt() current option */ + bool debug = false; + bool quiet = false; + double timeOut = DEFAULT_TIMEOUT; + // char fieldSeparator = ' '; + + /* + istream* inputStream = 0; + ifstream ifs; + bool fromStream = false; + */ + setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ + + while ((opt = getopt(argc, argv, ":hw:qdF:f:")) != -1) { + switch (opt) { + case 'h': /* Print usage */ + usage(); + return 0; + case 'w': /* Set PVA timeout value */ + if(epicsScanDouble(optarg, &timeOut) != 1 || timeOut <= 0.0) + { + fprintf(stderr, "'%s' is not a valid timeout value " + "- ignored. ('pvlist -h' for help.)\n", optarg); + timeOut = DEFAULT_TIMEOUT; + } + break; + case 'q': /* Quiet mode */ + quiet = true; + break; + case 'd': /* Debug log level */ + debug = true; + break; + /* + case 'F': // Store this for output formatting + fieldSeparator = (char) *optarg; + break; + case 'f': // Use input stream as input + { + string fileName = optarg; + if (fileName == "-") + inputStream = &cin; + else + { + ifs.open(fileName.c_str(), ifstream::in); + if (!ifs) + { + fprintf(stderr, + "Failed to open file '%s'.\n", + fileName.c_str()); + return 1; + } + else + inputStream = &ifs; + } + + fromStream = true; + break; + }*/ + case '?': + fprintf(stderr, + "Unrecognized option: '-%c'. ('pvlist -h' for help.)\n", + optopt); + return 1; + case ':': + fprintf(stderr, + "Option '-%c' requires an argument. ('pvlist -h' for help.)\n", + optopt); + return 1; + default : + usage(); + return 1; + } + } + + SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError); + + if (!quiet) + fprintf(stderr, "Searching...\n"); + + discoverServers(timeOut); + + if (!quiet) + fprintf(stderr, "done.\n"); + + return 0; +} diff --git a/pvtoolsSrc/pvutils.cpp b/pvtoolsSrc/pvutils.cpp index 1fbaccc..b3688b2 100644 --- a/pvtoolsSrc/pvutils.cpp +++ b/pvtoolsSrc/pvutils.cpp @@ -86,6 +86,12 @@ std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) case structureArray: return terseStructureArray(o, static_pointer_cast(pv)); break; + case union_: + return terseUnion(o, static_pointer_cast(pv)); + break; + case unionArray: + return terseUnionArray(o, static_pointer_cast(pv)); + break; default: std::ostringstream msg("unknown Field type: "); msg << type; @@ -95,6 +101,12 @@ std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) std::ostream& terseStructure(std::ostream& o, PVStructure::shared_pointer const & pvStructure) { + if (!pvStructure) + { + o << "(null)"; + return o; + } + PVFieldPtrArray fieldsData = pvStructure->getPVFields(); size_t length = pvStructure->getStructure()->getNumberFields(); bool first = true; @@ -109,6 +121,17 @@ std::ostream& terseStructure(std::ostream& o, PVStructure::shared_pointer const return o; } +std::ostream& terseUnion(std::ostream& o, PVUnion::shared_pointer const & pvUnion) +{ + if (!pvUnion || !pvUnion->get()) + { + o << "(null)"; + return o; + } + + return terse(o, pvUnion->get()); +} + std::ostream& terseScalarArray(std::ostream& o, PVScalarArray::shared_pointer const & pvArray) { size_t length = pvArray->getLength(); @@ -166,6 +189,31 @@ std::ostream& terseStructureArray(std::ostream& o, PVStructureArray::shared_poin return o; } +std::ostream& terseUnionArray(std::ostream& o, PVUnionArray::shared_pointer const & pvArray) +{ + size_t length = pvArray->getLength(); + if (arrayCountFlag) + { + if (length<=0) + { + o << '0'; + return o; + } + o << length << separator; + } + + PVUnionArray::const_svector data = pvArray->view(); + bool first = true; + for (size_t i = 0; i < length; i++) { + if (first) + first = false; + else + o << separator; + + terseUnion(o, data[i]); + } + return o; +} diff --git a/pvtoolsSrc/pvutils.h b/pvtoolsSrc/pvutils.h index a736d90..2f5041c 100644 --- a/pvtoolsSrc/pvutils.h +++ b/pvtoolsSrc/pvutils.h @@ -10,9 +10,11 @@ void convertStructureArray(std::string*, epics::pvData::PVStructureArray * pvdat void terseSeparator(char c); void terseArrayCount(bool flag); std::ostream& terse(std::ostream& o, epics::pvData::PVField::shared_pointer const & pv); +std::ostream& terseUnion(std::ostream& o, epics::pvData::PVUnion::shared_pointer const & pvUnion); std::ostream& terseStructure(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvStructure); std::ostream& terseScalarArray(std::ostream& o, epics::pvData::PVScalarArray::shared_pointer const & pvArray); std::ostream& terseStructureArray(std::ostream& o, epics::pvData::PVStructureArray::shared_pointer const & pvArray); +std::ostream& terseUnionArray(std::ostream& o, epics::pvData::PVUnionArray::shared_pointer const & pvArray); /* Converts a hex character to its integer value */ diff --git a/src/pva/pvaVersion.cpp b/src/pva/pvaVersion.cpp index f3d008e..036e8ea 100644 --- a/src/pva/pvaVersion.cpp +++ b/src/pva/pvaVersion.cpp @@ -54,7 +54,7 @@ bool Version::isDevelopmentVersion() const { const string Version::getVersionString() const { stringstream ret; - ret< +#endif + +#include + #include #include #include @@ -13,6 +19,7 @@ #include #include +#include #include #include @@ -454,6 +461,7 @@ private: static Structure::const_shared_pointer helpStructure; static Structure::const_shared_pointer channelListStructure; + static Structure::const_shared_pointer infoStructure; static std::string helpString; @@ -511,6 +519,44 @@ public: PVStringArray::shared_pointer pvArray = result->getSubField("value"); pvArray->replace(listListener->channelNames); + return result; + } + else if (op == "info") + { + PVStructure::shared_pointer result = + getPVDataCreate()->createPVStructure(infoStructure); + + // TODO cache hostname in InetAddressUtil + char buffer[256]; + std::string hostName("localhost"); + if (gethostname(buffer, sizeof(buffer)) == 0) + hostName = buffer; + + std::stringstream ret; + ret << EPICS_PVA_MAJOR_VERSION << '.' << + EPICS_PVA_MINOR_VERSION << '.' << + EPICS_PVA_MAINTENANCE_VERSION; + if (EPICS_PVA_DEVELOPMENT_FLAG) + ret << "-SNAPSHOT"; + + result->getSubField("version")->put(ret.str()); + result->getSubField("implLang")->put("cpp"); + result->getSubField("host")->put(hostName); + + std::stringstream sspid; +#ifdef __vxworks + sspid << taskIdSelf(); +#else + sspid << getpid(); +#endif + result->getSubField("process")->put(sspid.str()); + + char timeText[64]; + epicsTimeToStrftime(timeText, 64, "%Y-%m-%dT%H:%M:%S.%03f", &m_serverContext->getStartTime()); + + result->getSubField("startTime")->put(timeText); + + return result; } else @@ -531,14 +577,28 @@ Structure::const_shared_pointer ServerRPCService::channelListStructure = addArray("value", pvString)-> createStructure(); +Structure::const_shared_pointer ServerRPCService::infoStructure = + getFieldCreate()->createFieldBuilder()-> + add("process", pvString)-> + add("startTime", pvString)-> + add("version", pvString)-> + add("implLang", pvString)-> + add("host", pvString)-> +// add("os", pvString)-> +// add("arch", pvString)-> +// add("CPUs", pvInt)-> + createStructure(); + + std::string ServerRPCService::helpString = "pvAccess server RPC service.\n" "arguments:\n" "\tstring op\toperation to execute\n" "\n" "\toperations:\n" + "\t\tinfo\t\treturns some information about the server\n" "\t\tchannels\treturns a list of 'static' channels the server can provide\n" - "\t\t\t (no arguments)\n" +// "\t\t\t (no arguments)\n" "\n"; std::string ServerCreateChannelHandler::SERVER_CHANNEL_NAME = "server"; diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index a95e1be..fc3b453 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -40,9 +40,12 @@ ServerContextImpl::ServerContextImpl(): _channelProviderRegistry(), _channelProviderNames(PVACCESS_DEFAULT_PROVIDER), _channelProviders(), - _beaconServerStatusProvider() + _beaconServerStatusProvider(), + _startTime() { + epicsTimeGetCurrent(&_startTime); + // TODO maybe there is a better place for this (when there will be some factory) epicsSignalInstallSigAlarmIgnore (); epicsSignalInstallSigPipeIgnore (); @@ -674,6 +677,12 @@ void ServerContextImpl::newServerDetected() // not used } +epicsTimeStamp& ServerContextImpl::getStartTime() +{ + return _startTime; +} + + std::map >& ServerContextImpl::getSecurityPlugins() { return SecurityPluginRegistry::instance().getServerSecurityPlugins(); diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 0cf6116..7336bdb 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -92,6 +92,8 @@ public: */ virtual void dispose() = 0; + virtual epicsTimeStamp& getStartTime() = 0; + // ************************************************************************** // // **************************** [ Plugins ] ********************************* // // ************************************************************************** // @@ -146,6 +148,9 @@ public: BlockingUDPTransport::shared_pointer getLocalMulticastTransport(); + epicsTimeStamp& getStartTime(); + + /** * Version. */ @@ -440,6 +445,9 @@ private: void destroyAllTransports(); Configuration::shared_pointer configuration; + + epicsTimeStamp _startTime; + }; epicsShareExtern ServerContext::shared_pointer startPVAServer( diff --git a/src/utils/inetAddressUtil.cpp b/src/utils/inetAddressUtil.cpp index cef7390..4768b1b 100644 --- a/src/utils/inetAddressUtil.cpp +++ b/src/utils/inetAddressUtil.cpp @@ -70,7 +70,9 @@ bool decodeAsIPv6Address(ByteBuffer* buffer, osiSockAddr* address) { // first 80-bit are 0 if (buffer->getLong() != 0) return false; if (buffer->getShort() != 0) return false; - if (buffer->getShort() != (int16)0xFFFF) return false; + int16 ffff = buffer->getShort(); + // allow all zeros address + //if (ffff != (int16)0xFFFF) return false; uint32_t ipv4Addr = ((uint32_t)(buffer->getByte()&0xFF))<<24 | @@ -78,6 +80,9 @@ bool decodeAsIPv6Address(ByteBuffer* buffer, osiSockAddr* address) { ((uint32_t)(buffer->getByte()&0xFF))<<8 | ((uint32_t)(buffer->getByte()&0xFF)); + if (ffff != (int16)0xFFFF && ipv4Addr != (uint32_t)0) + return false; + address->ia.sin_addr.s_addr = htonl(ipv4Addr); return true;