/* * Copyright (C) 2018 Red Hat, Inc. * * Licensed under the GNU Lesser General Public License Version 2.1 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * This is example libdnf plugin. It works with applications that uses "context" libdnf API * (eg. microdnf and PackageKit). * The plugin writes information about repositories in the context and about packages in * the goal into the file. */ #define OUT_FILE_PATH "/tmp/libdnf_test_plugin.log" #include <libdnf/plugin/plugin.h> #include <libdnf/libdnf.h> #include <stdio.h> #include <stdlib.h> // Information about this plugin // Pointer to this structure is returned by pluginGetInfo(). static const PluginInfo info = { .name = "ExamplePlugin", .version = "1.0.0" }; // Plugin instance private data // Pointer to instance of this structure is returned by pluginInitHandle() as handle. struct _PluginHandle { PluginMode mode; DnfContext * context; // store plugin context specific init data FILE * outStream; // stream to write output }; // Returns general information about this plugin. // Can be called at any time. const PluginInfo * pluginGetInfo(void) { return &info; } // Creates new instance of this plugin. Returns its handle. PluginHandle * pluginInitHandle(int version, PluginMode mode, DnfPluginInitData * initData) { PluginHandle * handle = NULL; FILE * outStream = fopen(OUT_FILE_PATH, "a"); if (!outStream) return handle; do { fprintf(outStream, "===============================================================\n"); fprintf(outStream, "%s: %s: enter =========================\n", info.name, __func__); fprintf(outStream, "Plugin version=\"%s\", received API version=%i, received mode=\"%i\"\n", info.version, version, mode); if (version != 1) { fprintf(outStream, "%s: %s: Error: Unsupported API version\n", info.name, __func__); break; } if (mode != PLUGIN_MODE_CONTEXT) { fprintf(outStream, "%s: %s: Warning: Unsupported mode\n", info.name, __func__); break; } handle = malloc(sizeof(*handle)); handle->mode = mode; handle->context = pluginGetContext(initData); handle->outStream = outStream; } while (0); fprintf(outStream, "%s: %s: exit =========================\n", info.name, __func__); if (handle) fflush(outStream); else fclose(outStream); return handle; } // Destroys the plugin instance identified by given handle. void pluginFreeHandle(PluginHandle * handle) { if (handle) { fprintf(handle->outStream, "%s: %s: ===========================\n", info.name, __func__); fprintf(handle->outStream, "===============================================================\n\n"); fclose(handle->outStream); free(handle); } } // Writes a list of "packages". For each package is written a list of packages // thats are upgraded/downgraded/obsoleted by this package. These packages are added into // the "obsoleted" array. static void writeInfo(FILE * f, HyGoal goal, GPtrArray * packages, GPtrArray * obsoleted, const char * header, const char * obsoleteText) { if (packages->len == 0) return; fprintf(f, "%s", header); for (unsigned int i = 0; i < packages->len; ++i) { DnfPackage * pkg = g_ptr_array_index(packages, i); fprintf(f, " %s@%s\n", dnf_package_get_nevra(pkg), dnf_package_get_reponame(pkg)); // list of upgraded, downgraded, obsoleted packages g_autoptr(GPtrArray) obsoletedPackages = hy_goal_list_obsoleted_by_package(goal, pkg); for (unsigned int obsIdx = 0; obsIdx < obsoletedPackages->len; ++obsIdx) { DnfPackage * obsPkg = g_ptr_array_index(obsoletedPackages, obsIdx); fprintf(f, " %-25s%s\n", obsoleteText, dnf_package_get_nevra(obsPkg)); // Package can be obsoleted by more packages. // Do not add it into the "obsoleted" array more that once. gboolean found = FALSE; for (unsigned int allObsIdx = 0; allObsIdx < obsoleted->len; ++allObsIdx) { found = dnf_package_get_identical(g_ptr_array_index(obsoleted, allObsIdx), obsPkg); if (found) break; } if (!found) g_ptr_array_add(obsoleted, g_object_ref(obsPkg)); } } } int pluginHook(PluginHandle * handle, PluginHookId id, DnfPluginHookData * hookData, DnfPluginError * error) { if (!handle) return 1; fprintf(handle->outStream, "%s: %s: id=%i enter ========================\n", info.name, __func__, id); switch (id) { case PLUGIN_HOOK_ID_CONTEXT_PRE_TRANSACTION: fprintf(handle->outStream, "Info before libdnf context transaction run:\n"); // write info about loaded repos fprintf(handle->outStream, "Info about loaded repos:\n"); GPtrArray * repos = dnf_context_get_repos(handle->context); for (unsigned int i = 0; i < repos->len; ++i) { DnfRepo * repo = g_ptr_array_index(repos, i); const gchar * repoId = dnf_repo_get_id(repo); g_autofree gchar * description = dnf_repo_get_description(repo); bool enabled = (dnf_repo_get_enabled(repo) & DNF_REPO_ENABLED_PACKAGES) > 0; fprintf(handle->outStream, "Repo enabled=%i, repoId=%s, repoDescr=\"%s\"\n", enabled, repoId, description); } // write info about packages in goal fprintf(handle->outStream, "Info about packages in goal:\n"); HyGoal goal = hookContextTransactionGetGoal(hookData); if (goal) { // "obsoleted" array will be filled with obsoleted/updated/downgraded packages g_autoptr(GPtrArray) obsoleted = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); GPtrArray * packages = hy_goal_list_installs(goal, NULL); if (packages) { writeInfo(handle->outStream, goal, packages, obsoleted, "Install:\n", "obsoleted"); g_ptr_array_unref(packages); } packages = hy_goal_list_reinstalls(goal, NULL); if (packages) { writeInfo(handle->outStream, goal, packages, obsoleted, "Reinstall:\n", "obsoleted"); g_ptr_array_unref(packages); } packages = hy_goal_list_downgrades(goal, NULL); if (packages) { writeInfo(handle->outStream, goal, packages, obsoleted, "Downgrade:\n", "downgraded/obsoleted"); g_ptr_array_unref(packages); } packages = hy_goal_list_upgrades(goal, NULL); if (packages) { writeInfo(handle->outStream, goal, packages, obsoleted, "Upgrade:\n", "upgraded/obsoleted"); g_ptr_array_unref(packages); } packages = hy_goal_list_erasures(goal, NULL); if (packages) { if (packages->len) fprintf(handle->outStream, "Remove:\n"); for (unsigned int i = 0; i < packages->len; ++i) { DnfPackage * pkg = g_ptr_array_index(packages, i); fprintf(handle->outStream, " %s\n", dnf_package_get_nevra(pkg)); } g_ptr_array_unref(packages); } if (obsoleted->len) { fprintf(handle->outStream, "Summary of upgraded, downgraded, obsoleted:\n"); for (unsigned int i = 0; i < obsoleted->len; ++i) { DnfPackage * pkg = g_ptr_array_index(obsoleted, i); fprintf(handle->outStream, " %s\n", dnf_package_get_nevra(pkg)); g_autoptr(GPtrArray) obsoletedByPackages = hy_goal_list_obsoleted_by_package(goal, pkg); for (unsigned int obsIdx = 0; obsIdx < obsoletedByPackages->len; ++obsIdx) { DnfPackage * obsPkg = g_ptr_array_index(obsoletedByPackages, obsIdx); fprintf(handle->outStream, " %-25s%s@%s\n", "by", dnf_package_get_nevra(obsPkg), dnf_package_get_reponame(obsPkg)); } } } } break; default: break; } fprintf(handle->outStream, "%s: %s: id=%i exit ========================\n", info.name, __func__, id); fflush(handle->outStream); return 1; }