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