Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/evhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <system_error>
#include <deque>
#include <limits>
#include <list>
#include <algorithm>

#include <event2/event.h>
Expand Down Expand Up @@ -735,6 +736,7 @@ struct IfMapDaemon : private epicsThreadRunable {
epicsMutex lock;
epicsEvent wake;
std::shared_ptr<const IfaceMap::Current> latest;
std::list<std::function<void(const IfaceMap::Current&)>> listeners;
bool stop = false;
epicsThread worker;
IfMapDaemon()
Expand Down Expand Up @@ -765,7 +767,18 @@ struct IfMapDaemon : private epicsThreadRunable {
epicsGuardRelease<epicsMutex> U(G);
wake.wait(15.0); // arbitrary period...
next = IfaceMap::refresh();
if(*latest==*next)
continue;

for(auto& l : listeners) {
try {
l(*next); // next, previous
} catch (std::exception& e) {
log_exc_printf(logiface, "Unhandled exc %s : %s\n", typeid(e).name(), e.what());
}
}
}
log_warn_printf(logiface, "NIC configuration change detected %zu\n", next->byIndex.size());
latest.swap(next);

} catch(std::exception& e){
Expand Down Expand Up @@ -904,6 +917,46 @@ std::set<std::string> IfaceMap::all_external() const
return ret;
}

struct IfaceMap::Changer {
decltype(IfMapDaemon::listeners)::const_iterator it;
~Changer() {
Guard G(ifmapper->lock);
ifmapper->listeners.erase(it);
}
};

std::shared_ptr<IfaceMap::Changer>
IfaceMap::onChange(std::function<void (const IfaceMap::Current &)> &&fn)
{
auto ret(std::make_shared<Changer>());
threadOnce<&mapInit>();
assert(ifmapper);
Guard G(ifmapper->lock);
ret->it = ifmapper->listeners.insert(ifmapper->listeners.end(), std::move(fn));
return ret;
}

bool IfaceMap::Iface::operator==(const Iface &o) const
{
using P = decltype(addrs)::value_type;
auto comp = [](const P& l, const P& r) -> bool {
return l.first==r.first && l.second.compare(r.second, false)==0;
};
return name==o.name
&& index==o.index
&& isLO==o.isLO
&& addrs.size()==o.addrs.size()
&& std::equal(addrs.begin(), addrs.end(), o.addrs.begin(), comp)
&& bcast.size()==o.bcast.size()
&& std::equal(bcast.begin(), bcast.end(), o.bcast.begin(), comp);
}

bool IfaceMap::Current::operator==(const Current &o) const
{
return byIndex.size()==o.byIndex.size()
&& std::equal(byIndex.begin(), byIndex.end(), o.byIndex.begin());
// assume byName and byAddr indices are consistent
}

void to_wire(Buffer& buf, const SockAddr& val)
{
Expand Down
8 changes: 8 additions & 0 deletions src/evhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,16 +339,24 @@ struct PVXS_API IfaceMap {
// bcast - broadcast -> interface address
std::map<SockAddr, SockAddr, SockAddrOnlyLess> addrs, bcast;
Iface(const std::string& name, uint64_t index, bool isLO) :name(name), index(index), isLO(isLO) {}
bool operator==(const Iface& o) const;
bool operator!=(const Iface& o) const { return !((*this)==o); }
};

struct Current {
std::map<uint64_t, Iface> byIndex;
std::map<std::string, Iface*> byName;
// map address to tuple of interface and broadcast?
std::multimap<SockAddr, std::pair<Iface*, bool>, SockAddrOnlyLess> byAddr;
bool operator==(const Current& o) const;
bool operator!=(const Current& o) const { return !((*this)==o); }
};
std::shared_ptr<const Current> current;

struct Changer;
static
std::shared_ptr<Changer> onChange(std::function<void(const IfaceMap::Current&)>&& fn);

IfaceMap() = default;
IfaceMap(const IfaceMap&) = default;
IfaceMap(std::shared_ptr<const Current>&& cur) : current(std::move(cur)) {}
Expand Down
10 changes: 8 additions & 2 deletions src/osiSockExt.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,20 @@ struct PVXS_API SockAddr {
static SockAddr loopback(int af, unsigned port=0);

inline int compare(const SockAddr& o, bool useport=true) const {
if(family()<o.family())
return -1;
else if(family()>o.family())
return 1;
if(family()==AF_UNSPEC)
return 0;
return evutil_sockaddr_cmp(&store.sa, &o.store.sa, useport);
}

inline bool operator<(const SockAddr& o) const {
return evutil_sockaddr_cmp(&store.sa, &o.store.sa, true)<0;
return compare(o, true)<0;
}
inline bool operator==(const SockAddr& o) const {
return evutil_sockaddr_cmp(&store.sa, &o.store.sa, true)==0;
return compare(o, true)==0;
}
inline bool operator!=(const SockAddr& o) const {
return !(*this==o);
Expand Down
12 changes: 11 additions & 1 deletion src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,8 @@ std::ostream& operator<<(std::ostream& strm, const Server& serv)
}

Server::Pvt::Pvt(const Config &conf)
:effective(conf)
:original(conf)
,effective(conf)
,beaconMsg(128)
,acceptor_loop("PVXTCP", epicsThreadPriorityCAServerLow-2)
,beaconSender4(AF_INET, SOCK_DGRAM, 0)
Expand Down Expand Up @@ -545,6 +546,15 @@ Server::Pvt::Pvt(const Config &conf)
sources[std::make_pair(-1, "__server")] = std::make_shared<ServerSource>(this);
sources[std::make_pair(-1, "__builtin")] = builtinsrc.source();
}

ifChanged = IfaceMap::onChange([this](const IfaceMap::Current& cur) noexcept {
// on ifmap daemon thread
auto next(original);
// don't re-select if original was random
next.udp_port = effective.udp_port;
next.tcp_port = effective.tcp_port;
next.expand();
});
}

Server::Pvt::~Pvt()
Expand Down
3 changes: 2 additions & 1 deletion src/serverconn.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ struct Server::Pvt

std::weak_ptr<Server::Pvt> internal_self;

// "const" after ctor
const Config original;
std::shared_ptr<IfaceMap::Changer> ifChanged;
Config effective;

epicsEvent done;
Expand Down
Loading