Skip to content
Open
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
5 changes: 5 additions & 0 deletions modules/listDetector/src/listDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ ListDetector::ListDetector(const ConfigParser* configParser, ListDetectorMode mo
{
}

void ListDetector::updateRules(const ConfigParser* configParser)
{
m_rulesMatcher.updateRules(configParser);
}

bool ListDetector::matches(const Nemea::UnirecRecordView& unirecRecordView)
{
const bool match = m_rulesMatcher.anyOfRuleMatches(unirecRecordView);
Expand Down
6 changes: 6 additions & 0 deletions modules/listDetector/src/listDetector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class ListDetector {
*/
explicit ListDetector(const ConfigParser* configParser, ListDetectorMode mode);

/**
* @brief Updates rules in the ListDetector using the provided ConfigParser.
* @param configParser Pointer to the ConfigParser providing new rules.
*/
void updateRules(const ConfigParser* configParser);

/**
* @brief Checks if the given UnirecRecordView matches some rule from ListDetector.
* @param unirecRecordView The Unirec record to check against the ListDetector.
Expand Down
57 changes: 55 additions & 2 deletions modules/listDetector/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
#include <stdexcept>
#include <telemetry.hpp>
#include <unirec++/unirec.hpp>
#include <filesystem>

using namespace Nemea;

static std::atomic<bool> g_stopFlag(false);
static std::atomic<bool> g_rulesChanged(false);

static void signalHandler(int signum)
{
Expand Down Expand Up @@ -72,6 +74,37 @@ static void processNextRecord(
}
}

/**
* @brief Monitor the rules file for changes and set a flag when it changes.
*
* This function periodically checks the last modification time of the specified rules file.
* If rules has changed, the function sets a flag to indicate that the rules need to be reloaded.
*
* @param rulesFilePath Path to the rules file to monitor.
* @param checkIntervalms Interval in milliseconds between checks.
*/
static void checkRulesFileChanges(const std::string& rulesFilePath, int checkIntervalms)
{
auto logger = Nm::loggerGet("rulesFileWatcher");
try {
auto lastModificationTime = std::filesystem::last_write_time(rulesFilePath);

while (!g_stopFlag.load()) {
std::this_thread::sleep_for(std::chrono::milliseconds(checkIntervalms));
auto currentModificationTime = std::filesystem::last_write_time(rulesFilePath);
if (currentModificationTime != lastModificationTime) {
logger->warn("Rules file changed, reloading...");
lastModificationTime = currentModificationTime;
g_rulesChanged.store(true);
}
}

} catch (std::exception& ex) {
logger->error(ex.what());
g_stopFlag.store(true);
}
}

/**
* @brief Process Unirec records based on the rules of listDetector.
*
Expand All @@ -88,12 +121,13 @@ static void processUnirecRecords(
UnirecBidirectionalInterface& biInterface,
ListDetector::ListDetector& listDetector)
{
while (!g_stopFlag.load()) {
while (!g_stopFlag.load() && !g_rulesChanged.load()) {
try {
processNextRecord(biInterface, listDetector);
} catch (FormatChangeException& ex) {
handleFormatChange(biInterface);
} catch (const EoFException& ex) {
g_stopFlag.store(true);
break;
} catch (const std::exception& ex) {
throw;
Expand Down Expand Up @@ -124,6 +158,11 @@ int main(int argc, char** argv)
.required()
.help("path where the appFs directory will be mounted")
.default_value(std::string(""));

program.add_argument("-ci", "--check-interval")
.help("interval in milliseconds for checking rules file changes. Default is 10000ms (10s). Negative value disable checking.")
.default_value(10000)
.scan<'i', int>();
} catch (std::exception& ex) {
logger->error(ex.what());
return EXIT_FAILURE;
Expand Down Expand Up @@ -190,7 +229,21 @@ int main(int argc, char** argv)
auto listDetectorTelemetryDirectory = telemetryRootDirectory->addDir("listdetector");
listDetector.setTelemetryDirectory(listDetectorTelemetryDirectory);

processUnirecRecords(biInterface, listDetector);
if (program.get<int>("--check-interval") >= 0)
{
std::thread rulesFileWatcherThread(checkRulesFileChanges, program.get<std::string>("--rules"), program.get<int>("--check-interval"));
rulesFileWatcherThread.detach();
}

while (!g_stopFlag.load())
{
if(g_rulesChanged.load()) {
configParser= std::make_unique<ListDetector::CsvConfigParser>(program.get<std::string>("--rules"));
listDetector.updateRules(configParser.get());
g_rulesChanged.store(false);
}
processUnirecRecords(biInterface, listDetector);
}

} catch (std::exception& ex) {
logger->error(ex.what());
Expand Down
16 changes: 16 additions & 0 deletions modules/listDetector/src/rulesMatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ RulesMatcher::RulesMatcher(const ConfigParser* configParser) noexcept
m_fieldsMatcher = std::make_unique<FieldsMatcher>(m_rules);
}

void RulesMatcher::updateRules(const ConfigParser* configParser)
{
const std::string unirecTemplateDescription = configParser->getUnirecTemplateDescription();

RuleBuilder ruleBuilder(unirecTemplateDescription);

m_rules.clear();
for (const auto& ruleDescription : configParser->getRulesDescription()) {
auto rule = ruleBuilder.build(ruleDescription);
m_rules.emplace_back(rule);
}

m_ipAddressFieldMatchers = ruleBuilder.getIpAddressFieldMatchers();
m_fieldsMatcher = std::make_unique<FieldsMatcher>(m_rules);
}

std::vector<bool>
RulesMatcher::getMatchingIpRulesMask(const Nemea::UnirecRecordView& unirecRecordView)
{
Expand Down
6 changes: 6 additions & 0 deletions modules/listDetector/src/rulesMatcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ class RulesMatcher {
*/
explicit RulesMatcher(const ConfigParser* configParser) noexcept;

/**
* @brief Updates rules in the RulesMatcher using the provided ConfigParser.
* @param configParser Pointer to the ConfigParser providing new rules.
*/
void updateRules(const ConfigParser* configParser);

/**
* @brief Checks if some rule matches given Unirec view.
* @param unirecRecordView The Unirec view to match.
Expand Down
45 changes: 45 additions & 0 deletions modules/listDetector/tests/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ set -e
trap 'echo "Command \"$BASH_COMMAND\" failed!"; exit_with_error' ERR
for input_file in $data_path/inputs/*; do
index=$(echo "$input_file" | grep -o '[0-9]\+')
echo "Running test $index"

res_file="/tmp/res"
logger -i "u:listDetector" -w $res_file &
Expand Down Expand Up @@ -58,5 +59,49 @@ for input_file in $data_path/inputs/*; do
fi
done

echo "Running test 24 + 25"

res_file="/tmp/res"
logger -i "u:listDetector" -w $res_file &
logger_pid=$!
sleep 0.1

process_started $logger_pid

touch /tmp/rules
cat "$data_path/rules/rule24.csv" > /tmp/rules

$list_detector \
-i "u:lr,u:listDetector" \
-r "/tmp/rules" \
-lm blacklist \
-ci 100 &

detector_pid=$!
sleep 0.1
process_started $detector_pid

logreplay -i "u:lr" -f "$data_path/inputs/input24.csv" 2>/dev/null -n
sleep 1

cat "$data_path/rules/rule25.csv" > /tmp/rules

logreplay -i "u:lr" -f "$data_path/inputs/input25.csv" 2>/dev/null

wait $detector_pid
wait $logger_pid

cat "$data_path/results/res24.csv" "$data_path/results/res25.csv" > "/tmp/res_expected24_25.csv"

if [ -f "$res_file" ]; then
if ! cmp -s "/tmp/res_expected24_25.csv" "$res_file"; then
echo "Files /tmp/res_expected24_25.csv and $res_file are not equal"
exit_with_error
fi
else
echo "File $res_file not found"
exit_with_error
fi

echo "All tests passed"
exit 0