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

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