3. LIBDNF5 Plugin Template
Below is a template code for a LIBDNF5 plugin. For the complete tutorial on writing LIBDNF5 plugins, refer to LIBDNF5 Plugins.
libdnf5-plugins/template/template.cpp
1#include <libdnf5/base/base.hpp>
2#include <libdnf5/common/exception.hpp>
3#include <libdnf5/plugin/iplugin.hpp>
4
5#include <algorithm>
6#include <cstring>
7
8using namespace libdnf5;
9
10namespace {
11
12constexpr const char * PLUGIN_NAME{"libdnf5_template_plugin"};
13constexpr plugin::Version PLUGIN_VERSION{.major = 1, .minor = 1, .micro = 0};
14constexpr PluginAPIVersion REQUIRED_PLUGIN_API_VERSION{.major = 2, .minor = 2};
15
16constexpr const char * attrs[]{"author.name", "author.email", "description", nullptr};
17constexpr const char * attrs_value[]{"Fatima Freedom", "dummy@email.com", "Plugin description."};
18
19class TemplatePlugin final : public plugin::IPlugin2_2 {
20public:
21 /// Implement custom constructor for the new plugin.
22 /// This is not necessary when you only need Base object for your implementation.
23 /// Optional to override.
24 TemplatePlugin(libdnf5::plugin::IPluginData & data, libdnf5::ConfigParser &) : IPlugin2_2(data) {}
25
26 /// Fill in the API version of your plugin.
27 /// This is used to check if the provided plugin API version is compatible with the library's plugin API version.
28 /// MANDATORY to override.
29 PluginAPIVersion get_api_version() const noexcept override { return REQUIRED_PLUGIN_API_VERSION; }
30
31 /// Enter the name of your new plugin.
32 /// This is used in log messages when an action or error related to the plugin occurs.
33 /// MANDATORY to override.
34 const char * get_name() const noexcept override { return PLUGIN_NAME; }
35
36 /// Fill in the version of your plugin.
37 /// This is utilized in informative and debugging log messages.
38 /// MANDATORY to override.
39 plugin::Version get_version() const noexcept override { return PLUGIN_VERSION; }
40
41 /// Add custom attributes, such as information about yourself and a description of the plugin.
42 /// These can be used to query plugin-specific data through the API.
43 /// Optional to override.
44 const char * const * get_attributes() const noexcept override { return attrs; }
45 const char * get_attribute(const char * attribute) const noexcept override {
46 for (size_t i = 0; attrs[i]; ++i) {
47 if (std::strcmp(attribute, attrs[i]) == 0) {
48 return attrs_value[i];
49 }
50 }
51 return nullptr;
52 }
53
54 /// Initialization method called after the Base object is created and before command-line arguments are parsed.
55 /// Optional to override.
56 void init() override {}
57
58 /// Cleanup method called when plugin objects are garbage collected.
59 /// Optional to override.
60 void finish() noexcept override {}
61
62 /// Override the hooks you want to implement.
63 void post_base_setup() override { post_base_magic(); }
64 void pre_transaction(const libdnf5::base::Transaction & transaction) override {
65 pre_transaction_magic(transaction);
66 };
67
68private:
69 void post_base_magic();
70 void pre_transaction_magic(const libdnf5::base::Transaction &);
71};
72
73/// Example how to implement additional logic after the Base is set up.
74void TemplatePlugin::post_base_magic() {
75 const auto & tsflags = get_base().get_config().get_tsflags_option().get_value();
76 if (std::find(tsflags.begin(), tsflags.end(), "noscripts") != tsflags.end()) {
77 libdnf_throw_assertion("Hey, we don't want a transaction without scriptlets!");
78 }
79}
80
81/// Example how to implement additional logic before starting the transaction.
82void TemplatePlugin::pre_transaction_magic(const libdnf5::base::Transaction & transaction) {
83 auto & base = get_base();
84 auto & logger = *base.get_logger();
85 logger.info("Libdnf5 template plugin: {} packages in transaction", transaction.get_transaction_packages_count());
86}
87
88// Global variable to store the last exception from plugin initialization
89// Allows capturing exceptions from C linkage functions that cannot propagate C++ exceptions
90std::exception_ptr last_exception;
91
92} // namespace
93
94/// Below is a block of functions with C linkage used for loading the plugin binaries from disk.
95/// All of these are MANDATORY to implement.
96
97/// Return plugin's API version.
98PluginAPIVersion libdnf_plugin_get_api_version(void) {
99 return REQUIRED_PLUGIN_API_VERSION;
100}
101
102/// Return plugin's name.
103const char * libdnf_plugin_get_name(void) {
104 return PLUGIN_NAME;
105}
106
107/// Return plugin's version.
108plugin::Version libdnf_plugin_get_version(void) {
109 return PLUGIN_VERSION;
110}
111
112/// Return the instance of the implemented plugin.
113plugin::IPlugin * libdnf_plugin_new_instance(
114 [[maybe_unused]] LibraryVersion library_version,
115 libdnf5::plugin::IPluginData & data,
116 libdnf5::ConfigParser & parser) try {
117 return new TemplatePlugin(data, parser);
118} catch (...) {
119 // Capture any exception that occurs during plugin instance creation
120 // std::current_exception() retrieves the current exception and stores it for later processing
121 last_exception = std::current_exception();
122
123 return nullptr; // Return nullptr as failure indicator (C function cannot throw exceptions)
124}
125
126/// Delete the plugin instance.
127void libdnf_plugin_delete_instance(plugin::IPlugin * plugin_object) {
128 delete plugin_object;
129}
130
131// libdnf5 use this function to determine the actual failure cause
132// when libdnf_plugin_new_instance() returns nullptr
133std::exception_ptr * libdnf_plugin_get_last_exception(void) {
134 return &last_exception;
135}
libdnf5-plugins/template/CMakeLists.txt
1# set gettext domain for translations
2set(GETTEXT_DOMAIN libdnf5-plugin-template)
3add_definitions(-DGETTEXT_DOMAIN=\"${GETTEXT_DOMAIN}\")
4
5# add your source files
6add_library(template_plugin MODULE template.cpp)
7
8# disable the 'lib' prefix in order to create template.so
9set_target_properties(template_plugin PROPERTIES PREFIX "")
10
11# link the libdnf5 library
12target_link_libraries(template_plugin PRIVATE libdnf5)
13
14# install the plugin into the common libdnf5-plugins location
15#install(TARGETS template_plugin LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/libdnf5/plugins/")
16
17# install default plugin configuration file
18#install(FILES "template.conf" DESTINATION "${CMAKE_INSTALL_FULL_SYSCONFDIR}/dnf/libdnf5-plugins")
libdnf5-plugins/template/template.conf
1[main]
2name = template_plugin
3enabled = yes
4
5# We can provide optional custom keys and sections.
6custom_key = some_value
7
8[custom]
9website = https://my-hosting/template-plugin.org