Skip to content

Commit 0cb8070

Browse files
committed
ipfixprobe - refactor OutputPlugin
1 parent ea2cc3c commit 0cb8070

File tree

6 files changed

+292
-76
lines changed

6 files changed

+292
-76
lines changed

include/ipfixprobe/output.hpp

Lines changed: 0 additions & 74 deletions
This file was deleted.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @file
3+
* @brief Generic interface of output plugin
4+
* @author Pavel Siska <[email protected]>
5+
* @author Vaclav Bartos <[email protected]>
6+
* @author Jiri Havranek <[email protected]>
7+
* @date 2025
8+
*
9+
* Copyright (c) 2025 CESNET
10+
*
11+
* SPDX-License-Identifier: BSD-3-Clause
12+
*/
13+
14+
#pragma once
15+
16+
#include "api.hpp"
17+
#include "flowifc.hpp"
18+
#include "plugin.hpp"
19+
#include "process.hpp"
20+
21+
#include <cstdint>
22+
#include <string>
23+
#include <vector>
24+
25+
namespace ipxp {
26+
27+
#define DEFAULT_EXPORTER_ID 1
28+
29+
/**
30+
* \brief Base class for flow exporters.
31+
*/
32+
class IPXP_API OutputPlugin : public Plugin {
33+
public:
34+
typedef std::vector<std::pair<std::string, ProcessPlugin*>> Plugins;
35+
uint64_t m_flows_seen; /**< Number of flows received to export. */
36+
uint64_t m_flows_dropped; /**< Number of flows that could not be exported. */
37+
38+
OutputPlugin()
39+
: m_flows_seen(0)
40+
, m_flows_dropped(0)
41+
{
42+
}
43+
virtual ~OutputPlugin() {}
44+
45+
virtual void init(const char* params, Plugins& plugins) = 0;
46+
47+
enum class Result { EXPORTED = 0, DROPPED };
48+
/**
49+
* \brief Send flow record to output interface.
50+
* \param [in] flow Flow to send.
51+
* \return 0 on success
52+
*/
53+
virtual int export_flow(const Flow& flow) = 0;
54+
55+
/**
56+
* \brief Force exporter to flush flows to collector.
57+
*/
58+
virtual void flush() {}
59+
};
60+
61+
/**
62+
* @brief Factory template for creating plugins.
63+
*
64+
* This template allows dynamic creation of plugin instances based on the specified
65+
* base class and constructor argument types.
66+
*
67+
* @tparam Base The base class for the plugin.
68+
* @tparam Args The argument types required for the plugin constructor.
69+
*/
70+
template<typename Base, typename... Args>
71+
class IPXP_API PluginFactory;
72+
73+
/**
74+
* @brief Type alias for the OutputPlugin factory.
75+
*
76+
* Provides a factory for creating OutputPlugin instances using a string-based constructor.
77+
*/
78+
using OutputPluginFactory = PluginFactory<OutputPlugin, const std::string&>;
79+
80+
} // namespace ipxp

src/core/ipfixprobe.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
#include <appFs.hpp>
4343
#include <ipfixprobe/inputPlugin.hpp>
4444
#include <ipfixprobe/options.hpp>
45-
#include <ipfixprobe/output.hpp>
45+
#include <ipfixprobe/outputPlugin.hpp>
4646
#include <ipfixprobe/packet.hpp>
4747
#include <ipfixprobe/plugin.hpp>
4848
#include <ipfixprobe/process.hpp>

src/core/pluginManager.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* @file
3+
* @brief Implementation of PluginManager class for loading and unloading plugins.
4+
* @author Pavel Siska <[email protected]>
5+
* @date 2025
6+
*
7+
* Copyright (c) 2025 CESNET
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*/
11+
12+
#include "pluginManager.hpp"
13+
14+
#include <filesystem>
15+
#include <iostream>
16+
#include <stdexcept>
17+
#include <variant>
18+
19+
#include <dlfcn.h>
20+
21+
namespace ipxp {
22+
23+
namespace fs = std::filesystem;
24+
25+
static void clearErrorMessage()
26+
{
27+
dlerror();
28+
}
29+
30+
static void* openSharedObject(const std::string& path)
31+
{
32+
const int dlFlags = RTLD_LAZY | RTLD_LOCAL;
33+
return dlopen(path.c_str(), dlFlags);
34+
}
35+
36+
template<typename Iterator>
37+
static bool isValidPlugin(const Iterator& entry)
38+
{
39+
return entry.is_regular_file() && entry.path().extension() == ".so";
40+
}
41+
42+
static void loadPluginsRecursive(const std::string& dirPath, PluginManager& pluginManager)
43+
{
44+
for (const auto& entry : fs::recursive_directory_iterator(dirPath)) {
45+
if (isValidPlugin(entry)) {
46+
pluginManager.loadPlugin(entry.path().string());
47+
}
48+
}
49+
}
50+
51+
static void loadPluginsNonRecursive(const std::string& dirPath, PluginManager& pluginManager)
52+
{
53+
for (const auto& entry : fs::directory_iterator(dirPath)) {
54+
if (isValidPlugin(entry)) {
55+
pluginManager.loadPlugin(entry.path().string());
56+
}
57+
}
58+
}
59+
60+
PluginManager::PluginManager(bool unloadAtExit)
61+
: m_unloadAtExit(unloadAtExit)
62+
{
63+
}
64+
65+
void PluginManager::loadPlugins(const std::string& dirPath, bool recursive)
66+
{
67+
const auto loadPluginsFunction = recursive ? loadPluginsRecursive : loadPluginsNonRecursive;
68+
69+
try {
70+
loadPluginsFunction(dirPath, *this);
71+
} catch (const fs::filesystem_error& ex) {
72+
std::cerr << "Error accessing directory '" << dirPath << "': " << ex.what() << std::endl;
73+
// m_logger->error("Error accessing directory '{}': {}", dirPath, ex.what());
74+
throw std::runtime_error("PluginManager::loadPlugins() has failed.");
75+
}
76+
}
77+
78+
void PluginManager::loadPlugin(const std::string& pluginPath)
79+
{
80+
clearErrorMessage();
81+
void* pluginHandle = openSharedObject(pluginPath);
82+
83+
if (!pluginHandle) {
84+
std::cerr << "Error loading plugin '" << pluginPath << "': " << dlerror() << std::endl;
85+
// m_logger->error(std::string(dlerror()));
86+
throw std::runtime_error("PluginManager::loadPlugin() has failed.");
87+
}
88+
89+
std::cerr << "Plugin '" << pluginPath << "' loaded." << std::endl;
90+
// m_logger->info("Plugin '{}' loaded.", pluginPath);
91+
92+
m_pluginHandles.emplace_back(pluginHandle);
93+
}
94+
95+
PluginManager::~PluginManager()
96+
{
97+
if (m_unloadAtExit) {
98+
unloadPlugins();
99+
}
100+
}
101+
102+
void PluginManager::unloadPlugins()
103+
{
104+
for (auto handle : m_pluginHandles) {
105+
dlclose(handle);
106+
}
107+
m_pluginHandles.clear();
108+
}
109+
110+
} // namespace ipxp

src/core/pluginManager.hpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* @file
3+
* @brief Manages the loading and unloading of shared plugins in the PluginManager class.
4+
*
5+
* This class provides functionality for loading dynamic shared objects (plugins) from specified
6+
* directories, optionally searching them recursively. It supports unloading plugins either
7+
* automatically at exit or manually through a function call. The class uses the `dlopen()` and
8+
* `dlclose()` system calls for managing shared object libraries, and the `std::filesystem` library
9+
* to iterate over files in a directory.
10+
*
11+
* @author Pavel Siska <[email protected]>
12+
* @date 2025
13+
*
14+
* Copyright (c) 2025 CESNET
15+
*
16+
* SPDX-License-Identifier: BSD-3-Clause
17+
*/
18+
19+
#pragma once
20+
21+
#include <string>
22+
#include <vector>
23+
24+
namespace ipxp {
25+
26+
/**
27+
* @brief Manages loading and unloading of plugins (shared objects).
28+
*
29+
* This class loads plugins from a specified directory (with optional recursion), unloads them,
30+
* and manages their state. Plugins are loaded with `dlopen()` and unloaded with `dlclose()`.
31+
*
32+
* The `unloadAtExit` flag determines if plugins should be automatically unloaded when the
33+
* `PluginManager` object is destroyed. If set to `false`, plugins remain loaded, which is useful
34+
* for debugging (e.g., with Valgrind).
35+
*
36+
* It can load plugins from individual files or all `.so` files in a directory (recursively).
37+
*/
38+
class PluginManager {
39+
public:
40+
/**
41+
* @brief Constructor for PluginManager.
42+
*
43+
* Initializes the PluginManager, optionally setting whether plugins should be unloaded
44+
* automatically when the PluginManager object is destroyed (in the destructor).
45+
*
46+
* @param unloadAtExit A boolean flag that determines if plugins should be unloaded when the
47+
* PluginManager object is destroyed. Defaults to `true`.
48+
*/
49+
PluginManager(bool unloadAtExit = true);
50+
51+
/**
52+
* @brief Destructor for PluginManager.
53+
*
54+
* If the `unloadAtExit` flag is set to `true`, this method will automatically unload
55+
* all loaded plugins by calling `dlclose()` on each plugin handle.
56+
*/
57+
~PluginManager();
58+
59+
/**
60+
* @brief Load a plugin from a specified file path.
61+
*
62+
* This method loads a single plugin (shared object) from the provided file path. If the plugin
63+
* cannot be loaded, it will log an error and throw a `std::runtime_error`.
64+
*
65+
* @param pluginPath The path to the plugin file (shared object).
66+
* @throws std::runtime_error if loading the plugin fails.
67+
*/
68+
void loadPlugin(const std::string& pluginPath);
69+
70+
/**
71+
* @brief Load plugins from a specified directory.
72+
*
73+
* This method loads all `.so` plugins from the specified directory. Optionally, it can
74+
* search subdirectories recursively for plugins. It uses `std::filesystem` to iterate over
75+
* the directory contents and attempts to load each `.so` file as a plugin. If an error occurs
76+
* while accessing the directory or loading a plugin, it will log the error and throw an
77+
* exception.
78+
*
79+
* @param dirPath The path to the directory containing the plugins.
80+
* @param recursive A boolean flag indicating whether to search subdirectories recursively.
81+
*
82+
* @throws std::runtime_error If there is an error accessing the directory or loading the
83+
* plugins.
84+
*/
85+
void loadPlugins(const std::string& dirPath, bool recursive = false);
86+
87+
/**
88+
* @brief Unload all loaded plugins.
89+
*
90+
* This method unloads all currently loaded plugins by calling `dlclose()` on each plugin
91+
* handle.
92+
*/
93+
void unloadPlugins();
94+
95+
private:
96+
std::vector<void*> m_pluginHandles;
97+
bool m_unloadAtExit;
98+
};
99+
100+
} // namespace ipxp

src/core/workers.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
#include <future>
3636

3737
#include <ipfixprobe/inputPlugin.hpp>
38-
#include <ipfixprobe/output.hpp>
38+
#include <ipfixprobe/outputPlugin.hpp>
3939
#include <ipfixprobe/packet.hpp>
4040
#include <ipfixprobe/process.hpp>
4141
#include <ipfixprobe/ring.h>

0 commit comments

Comments
 (0)