Add event for network interfaces change.

This commit is contained in:
Peter Johnson
2016-11-18 12:38:54 -08:00
parent 5fecc57e8a
commit bad4ca4666
8 changed files with 195 additions and 3 deletions

View File

@@ -121,7 +121,8 @@ enum CS_EventKind {
CS_SINK_CREATED = 0x0400,
CS_SINK_DESTROYED = 0x0800,
CS_SINK_ENABLED = 0x1000,
CS_SINK_DISABLED = 0x2000
CS_SINK_DISABLED = 0x2000,
CS_NETWORK_INTERFACES_CHANGED = 0x4000
};
//

View File

@@ -81,10 +81,12 @@ struct RawEvent {
kSinkCreated = CS_SINK_CREATED,
kSinkDestroyed = CS_SINK_DESTROYED,
kSinkEnabled = CS_SINK_ENABLED,
kSinkDisabled = CS_SINK_DISABLED
kSinkDisabled = CS_SINK_DISABLED,
kNetworkInterfacesChanged = CS_NETWORK_INTERFACES_CHANGED
};
RawEvent() = default;
RawEvent(RawEvent::Kind kind_) : kind{kind_} {}
RawEvent(llvm::StringRef name_, CS_Handle handle_, RawEvent::Kind kind_)
: kind{kind_}, name{name_} {
if (kind_ == kSinkCreated || kind_ == kSinkDestroyed ||

View File

@@ -24,7 +24,8 @@ public class VideoEvent {
kSinkCreated(0x0400),
kSinkDestroyed(0x0800),
kSinkEnabled(0x1000),
kSinkDisabled(0x2000);
kSinkDisabled(0x2000),
kNetworkInterfacesChanged(0x4000);
private int value;
@@ -53,6 +54,7 @@ public class VideoEvent {
case 0x0800: return Kind.kSinkDestroyed;
case 0x1000: return Kind.kSinkEnabled;
case 0x2000: return Kind.kSinkDisabled;
case 0x4000: return Kind.kNetworkInterfacesChanged;
default: return Kind.kUnknown;
}
}

135
src/NetworkListener.cpp Normal file
View File

@@ -0,0 +1,135 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015-2016. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "NetworkListener.h"
#ifdef __linux__
#include <errno.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/eventfd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include "Log.h"
#include "Notifier.h"
using namespace cs;
ATOMIC_STATIC_INIT(NetworkListener)
class NetworkListener::Thread : public wpi::SafeThread {
public:
void Main();
#ifdef __linux__
int m_command_fd = -1;
#endif
};
NetworkListener::~NetworkListener() { Stop(); }
void NetworkListener::Start() {
auto thr = m_owner.GetThread();
if (!thr) m_owner.Start();
}
void NetworkListener::Stop() {
// Wake up thread
if (auto thr = m_owner.GetThread()) {
thr->m_active = false;
#ifdef __linux__
if (thr->m_command_fd >= 0) eventfd_write(thr->m_command_fd, 1);
#endif
}
m_owner.Stop();
}
void NetworkListener::Thread::Main() {
#ifdef __linux__
// Create event socket so we can be shut down
m_command_fd = ::eventfd(0, 0);
if (m_command_fd < 0) {
ERROR("NetworkListener: could not create eventfd: " << strerror(errno));
return;
}
// Create netlink socket
int sd = ::socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sd < 0) {
ERROR("NetworkListener: could not create socket: " << strerror(errno));
::close(m_command_fd);
m_command_fd = -1;
return;
}
// Bind to netlink socket
struct sockaddr_nl addr;
std::memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
if (bind(sd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
ERROR("NetworkListener: could not create socket: " << strerror(errno));
::close(sd);
::close(m_command_fd);
m_command_fd = -1;
return;
}
char buf[4096];
while (m_active) {
// select timeout
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
// select on applicable read descriptors
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(m_command_fd, &readfds);
FD_SET(sd, &readfds);
int nfds = std::max(m_command_fd, sd) + 1;
if (::select(nfds, &readfds, nullptr, nullptr, &tv) < 0) {
ERROR("NetworkListener: select(): " << strerror(errno));
break; // XXX: is this the right thing to do here?
}
// Double-check to see if we're shutting down
if (!m_active) break;
if (!FD_ISSET(sd, &readfds)) continue;
std::memset(&addr, 0, sizeof(addr));
struct iovec iov = {buf, sizeof(buf)};
struct msghdr msg = {&addr, sizeof(addr), &iov, 1, nullptr, 0, 0};
int len = ::recvmsg(sd, &msg, 0);
if (len < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN) continue;
ERROR("NetworkListener: could not read netlink: " << strerror(errno));
break; // XXX: is this the right thing to do here?
}
if (len == 0) continue; // EOF?
for (struct nlmsghdr* nh = reinterpret_cast<struct nlmsghdr*>(buf);
NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_type == NLMSG_DONE) break;
if (nh->nlmsg_type == RTM_NEWLINK || nh->nlmsg_type == RTM_DELLINK ||
nh->nlmsg_type == RTM_NEWADDR || nh->nlmsg_type == RTM_DELADDR) {
Notifier::GetInstance().NotifyNetworkInterfacesChanged();
}
}
}
::close(sd);
::close(m_command_fd);
m_command_fd = -1;
#endif
}

38
src/NetworkListener.h Normal file
View File

@@ -0,0 +1,38 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015-2016. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef CS_NETWORKLISTENER_H_
#define CS_NETWORKLISTENER_H_
#include "support/atomic_static.h"
#include "support/SafeThread.h"
namespace cs {
class NetworkListener {
public:
static NetworkListener& GetInstance() {
ATOMIC_STATIC(NetworkListener, instance);
return instance;
}
~NetworkListener();
void Start();
void Stop();
private:
NetworkListener() = default;
class Thread;
wpi::SafeThreadOwner<Thread> m_owner;
ATOMIC_STATIC_DECL(NetworkListener)
};
} // namespace cs
#endif // CS_NETWORKLISTENER_H_

View File

@@ -216,3 +216,11 @@ void Notifier::NotifySinkSourceChanged(llvm::StringRef name, CS_Sink sink,
thr->m_notifications.emplace(std::move(event));
thr->m_cond.notify_one();
}
void Notifier::NotifyNetworkInterfacesChanged() {
auto thr = m_owner.GetThread();
if (!thr) return;
thr->m_notifications.emplace(RawEvent::kNetworkInterfacesChanged);
thr->m_cond.notify_one();
}

View File

@@ -52,6 +52,7 @@ class Notifier {
void NotifySink(const SinkImpl& sink, CS_EventKind kind);
void NotifySinkSourceChanged(llvm::StringRef name, CS_Sink sink,
CS_Source source);
void NotifyNetworkInterfacesChanged();
private:
Notifier();

View File

@@ -16,6 +16,7 @@
#include "llvm/SmallString.h"
#include "NetworkListener.h"
#include "Notifier.h"
#include "SinkImpl.h"
#include "SourceImpl.h"
@@ -487,6 +488,10 @@ CS_Listener AddListener(std::function<void(const RawEvent& event)> callback,
int eventMask, bool immediateNotify,
CS_Status* status) {
int uid = Notifier::GetInstance().AddListener(callback, eventMask);
if ((eventMask & CS_NETWORK_INTERFACES_CHANGED) != 0) {
// start network interface event listener
NetworkListener::GetInstance().Start();
}
if (immediateNotify) {
// TODO
}