Skip to content

Commit

Permalink
Added means to reconfigure the running instance of nettracer.
Browse files Browse the repository at this point in the history
Added an --args_file <path> arg to load configuration from file rather
than cmd line args. The configuration is reloaded live when the config
file is updated by means of rename(2)/mv(1). For this purpose,
IN_MOVED_TO event is configured by means of inotify_add_watch(2).

The args file format is boost::program_options::parse_config_file()
compatible.

Signed-off-by: Lukasz Lasek <[email protected]>
  • Loading branch information
llasek-dt committed Jan 8, 2025
1 parent 5d8bc1f commit 081f8b6
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 18 deletions.
4 changes: 4 additions & 0 deletions libnettracer/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
set(SOURCES
bpf_events.cpp
config_watcher.cpp
connections_printing.cpp
inotify_watcher.cpp
localsock.cpp
localsock6.cpp
offsetguess.cpp
Expand All @@ -11,7 +13,9 @@ set(SOURCES

set(HEADERS
bpf_events.h
config_watcher.h
connections_printing.h
inotify_watcher.h
localsock.h
localsock6.h
offsetguess.h
Expand Down
9 changes: 9 additions & 0 deletions libnettracer/src/bpf_events.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "bpf_events.h"
#include "bpf_generic/src/perf_event.h"
#include "config_watcher.h"
#include <algorithm>
#include <exception>
#include <iostream>
Expand All @@ -26,6 +27,9 @@ std::vector<pollfd> bpf_events::create_pfds() {
for (const auto& ito : observers) {
std::transform(ito.md.pfd.begin(), ito.md.pfd.end(), std::back_inserter(fds), [](auto& it) { return pollfd{it, POLLIN, 0}; });
}
if (cw) {
fds.push_back(pollfd{cw.get_poll_fd(), POLLIN, 0});
}
return fds;
}

Expand Down Expand Up @@ -53,6 +57,11 @@ void bpf_events::loop() {
exit(1);
}
kbhit_observer();
} else if (fd.fd == cw.get_poll_fd()) {
cw.on_pollin();
if (cw.is_config_changed()) {
config_change_observer();
}
} else {

auto ac = fd_to_evtype(fd.fd);
Expand Down
11 changes: 11 additions & 0 deletions libnettracer/src/bpf_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <variant>
#include <vector>

class config_watcher;

struct pollfd;

template <typename T>
Expand All @@ -25,8 +27,12 @@ class bpf_events {
std::vector<evt_descr> observers;
std::vector<pollfd> create_pfds();
std::function<void()> kbhit_observer;
std::function<void()> config_change_observer;
config_watcher& cw;

public:
bpf_events(config_watcher& cw) : cw(cw) {}

template <typename T>
void add_observer(const bpf::map_data md, f_ac<T> ac) {
evt_descr tmp;
Expand All @@ -38,6 +44,11 @@ class bpf_events {
void set_kbhit_observer(std::function<void()>&& f) {
kbhit_observer = f;
}

void set_config_change_observer(std::function<void()>&& f) {
config_change_observer = f;
}

void start();
void stop();
void loop();
Expand Down
65 changes: 65 additions & 0 deletions libnettracer/src/config_watcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "config_watcher.h"

#include <filesystem>
#include <functional>
#include <stdexcept>
#include <system_error>

using namespace std::literals::string_literals;

void config_watcher::init(std::string file_path) {
if (iw_token) {
throw std::logic_error("config_watcher already initialized");
}
if (file_path.empty()) {
return;
}

std::filesystem::path full_path(file_path);
dir_path = full_path.parent_path();
file_name = full_path.filename();
iw_token = iw.watch_path(dir_path, IN_MOVED_TO, std::bind(&config_watcher::on_event, this, std::placeholders::_1));
if (!iw_token) {
throw std::system_error(errno, std::system_category(), dir_path.c_str());
}
}

config_watcher::operator bool() {
return iw_token;
}

int config_watcher::get_poll_fd() {
return iw.get_poll_fd();
}

void config_watcher::on_pollin() {
if (!iw_token) {
return;
}
iw.on_pollin();
}

void config_watcher::on_event(inotify_event& ie) {
if (!(ie.mask & IN_MOVED_TO)) {
return;
}
if (!ie.len) {
return;
}
if (ie.wd != iw_token.wd) {
return;
}
if (file_name != ie.name) {
return;
}

config_changed = true;
}

bool config_watcher::is_config_changed() {
return config_changed;
}

void config_watcher::reset() {
config_changed = false;
}
34 changes: 34 additions & 0 deletions libnettracer/src/config_watcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include "inotify_watcher.h"

#include <boost/noncopyable.hpp>
#include <filesystem>
#include <functional>
#include <string>

class config_watcher : private boost::noncopyable {
public:
config_watcher() = default;
~config_watcher() = default;

void init(std::string file_path);

operator bool();
int get_poll_fd();

void on_pollin();

bool is_config_changed();

void reset();

protected:
void on_event(inotify_event& ie);

bool config_changed{false};
inotify_watcher iw{};
inotify_watcher::watch_token iw_token{};
std::filesystem::path dir_path{};
std::filesystem::path file_name{};
};
80 changes: 80 additions & 0 deletions libnettracer/src/inotify_watcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "inotify_watcher.h"

#include <system_error>
#include <unistd.h>
#include <utility>

inotify_watcher::inotify_watcher() {
fd = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (fd == -1) {
throw std::system_error(errno, std::system_category(), "inotify_init failed");
}
}

inotify_watcher::~inotify_watcher() {
for (auto& wd : wds) {
unwatch_path({wd.first});
}
::close(fd);
}

inotify_watcher::watch_token inotify_watcher::watch_path(std::string path, uint32_t inotify_mask, watch_callback wc) {
if (fd == -1) {
return {-1};
}
int wd = ::inotify_add_watch(fd, path.c_str(), inotify_mask);
if (wd == -1) {
return {-1};
}
wds.insert(std::make_pair(wd, wc));
return {wd};
}

bool inotify_watcher::unwatch_path(watch_token token) {
if (!token) {
return false;
}
if (fd == -1) {
return false;
}
if (wds.find(token.wd) == wds.end()) {
return false;
}
int rc = ::inotify_rm_watch(fd, token.wd);
if (rc == -1) {
return false;
}
wds.erase(token.wd);
return true;
}

inotify_watcher::operator bool() {
return fd != -1;
}

int inotify_watcher::get_poll_fd() {
return fd;
}

void inotify_watcher::on_pollin() {
inotify_event events[64];
while (true) {
auto len = ::read(fd, &events, sizeof(events));
if (len == -1) {
break;
}
dispatch(events, len / sizeof(*events));
}
}

void inotify_watcher::dispatch(inotify_event* events, size_t count) {
while (count) {
auto wd = wds.find(events->wd);
if (wd != wds.end()) {
wd->second(*events);
}

++events;
--count;
}
}
36 changes: 36 additions & 0 deletions libnettracer/src/inotify_watcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <functional>
#include <map>
#include <string>
#include <sys/inotify.h>

class inotify_watcher {
public:
using watch_callback = std::function<void(inotify_event&)>;

struct watch_token {
operator bool() { return wd != -1; };
int wd{-1};
};

inotify_watcher();
~inotify_watcher();

watch_token watch_path(std::string path, uint32_t inotify_mask, watch_callback wc);
bool unwatch_path(watch_token token);

operator bool();
int get_poll_fd();

void on_pollin();

protected:
void dispatch(inotify_event* events, size_t count);

// inotify fd
int fd{-1};

// inotify watch descriptors
std::map<int, watch_callback> wds;
};
14 changes: 11 additions & 3 deletions libnettracer/src/netstat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ std::pair<unsigned, unsigned> NetStat::countTcpSessions() {
return std::pair<unsigned, unsigned>{incoming, container4.size() + container6.size() - incoming};
}

void NetStat::map_loop(const bpf_fds& fdsIPv4, const bpf_fds& fdsIPv6) {
bool NetStat::map_loop(const bpf_fds& fdsIPv4, const bpf_fds& fdsIPv6) {
using namespace std::literals::chrono_literals;

TimeGuard outputCtr(exitCtrl.wait_time), logCtr(seconds(5min).count());
printHeader();

while (exitCtrl.running) {
while (exitCtrl.running && !config_changed) {
update<ipv4_tuple_t>(fdsIPv4);
update<ipv6_tuple_t>(fdsIPv6);

Expand Down Expand Up @@ -160,8 +160,9 @@ void NetStat::map_loop(const bpf_fds& fdsIPv4, const bpf_fds& fdsIPv6) {
logCtr.bump();
std::unique_lock<std::mutex> lk(exitCtrl.m);
kbhit = false;
exitCtrl.cv.wait_for(lk, milliseconds(1000 / INTERVAL_DIVIDER), [this] { return !exitCtrl.running || kbhit; });
exitCtrl.cv.wait_for(lk, milliseconds(1000 / INTERVAL_DIVIDER), [this] { return !exitCtrl.running || kbhit || config_changed; });
}
return exitCtrl.running;
}

template<typename IPTYPE>
Expand Down Expand Up @@ -373,4 +374,11 @@ void NetStat::set_kbhit() {
exitCtrl.cv.notify_all();
}

void NetStat::on_config_change() {
std::unique_lock<std::mutex> ul(exitCtrl.m);
config_changed = true;
ul.unlock();
exitCtrl.cv.notify_all();
}

} // namespace netstat
4 changes: 3 additions & 1 deletion libnettracer/src/netstat.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class NetStat {
ConnectionsIPv6 aggr6_;
std::mutex mx;
bool kbhit;
bool config_changed{false};
const int max_map_size = 1024;
ExitCtrl& exitCtrl;
bool incremental;
Expand Down Expand Up @@ -93,9 +94,10 @@ class NetStat {
explicit NetStat(ExitCtrl& e, bool deltaMode, bool headerMode, bool nonInteractive, bool filterLoopback = true);
virtual ~NetStat();
void set_kbhit();
void on_config_change();
void init();

void map_loop(const bpf_fds& fdsIPv4, const bpf_fds& fdsIPv6);
bool map_loop(const bpf_fds& fdsIPv4, const bpf_fds& fdsIPv6);

template<typename IPTYPE, typename EventIPTYPE>
void event(const EventIPTYPE& evt);
Expand Down
Loading

0 comments on commit 081f8b6

Please sign in to comment.