mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-30 02:31:44 +00:00
Add event for network interfaces change.
This commit is contained in:
@@ -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
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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
135
src/NetworkListener.cpp
Normal 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
38
src/NetworkListener.h
Normal 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_
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user