6.9 KiB
libdnf Integration in rpm-ostree: Deep Analysis
Executive Summary
rpm-ostree integrates libdnf as its core RPM package management engine, but with significant customizations and architectural adaptations to support its hybrid image/package model. The integration is sophisticated and goes far beyond simple "scripting" - it represents a deep architectural bridge between traditional RPM package management and modern image-based deployments.
libdnf Architecture Overview
1. Core C Library Foundation
libdnf is fundamentally a C library that provides a comprehensive C API for RPM package management:
// Core libdnf C API types
typedef struct _DnfContext DnfContext;
typedef struct _DnfPackage DnfPackage;
typedef struct _DnfRepo DnfRepo;
typedef struct _DnfSack DnfSack;
typedef struct _DnfGoal DnfGoal;
2. C API Functions
The library exposes C functions for all package management operations:
// Context management
DnfContext *dnf_context_new(void);
void dnf_context_set_repo_dir(DnfContext *ctx, const char *reposdir);
void dnf_context_set_cache_dir(DnfContext *ctx, const char *cachedir);
// Package operations
DnfPackage *dnf_sack_add_cmdline_package(DnfSack *sack, const char *filename);
const char *dnf_package_get_name(DnfPackage *pkg);
const char *dnf_package_get_nevra(DnfPackage *pkg);
// Repository management
GPtrArray *dnf_context_get_repos(DnfContext *ctx);
const char *dnf_repo_get_id(DnfRepo *repo);
3. GObject Integration
libdnf uses GObject for object lifecycle management, which is a C-based object system:
// GObject-based inheritance
G_DEFINE_TYPE(DnfContext, dnf_context, G_TYPE_OBJECT);
G_DEFINE_TYPE(DnfPackage, dnf_package, G_TYPE_OBJECT);
4. Key Characteristics
- C Foundation: libdnf is fundamentally a C library with a C API
- GObject System: Uses GObject for object-oriented features in C
- Multi-Language Support: Can be wrapped for C++, Rust, Python, etc.
- RPM Integration: Deeply integrated with the RPM package format and librpm
- Dependency Resolution: Uses libsolv for sophisticated dependency resolution
Core Integration Architecture
1. RpmOstreeContext: The Bridge Layer
The primary integration point is the RpmOstreeContext structure, which wraps and customizes libdnf's DnfContext:
struct _RpmOstreeContext {
GObject parent;
DnfContext *dnfctx; // Core libdnf context
// ... extensive customization fields
};
Key Customizations:
- Repository Management: Custom repo configuration from OSTree deployments
- Package Caching: Integration with OSTree pkgcache repository
- Transaction Control: Disabled disk space checks, transaction validation
- Plugin System: Disabled libdnf plugins in favor of rpm-ostree's own system
2. Context Initialization Pattern
// From rpmostree_context_new_base()
self->dnfctx = dnf_context_new();
dnf_context_set_repo_dir(self->dnfctx, "/etc/yum.repos.d");
dnf_context_set_cache_dir(self->dnfctx, RPMOSTREE_CORE_CACHEDIR RPMOSTREE_DIR_CACHE_REPOMD);
dnf_context_set_solv_dir(self->dnfctx, RPMOSTREE_CORE_CACHEDIR RPMOSTREE_DIR_CACHE_SOLV);
dnf_context_set_lock_dir(self->dnfctx, "/run/rpm-ostree/" RPMOSTREE_DIR_LOCK);
dnf_context_set_user_agent(self->dnfctx, PACKAGE_NAME "/" PACKAGE_VERSION);
// Critical customizations
dnf_context_set_write_history(self->dnfctx, FALSE); // No SWDB
dnf_context_set_check_disk_space(self->dnfctx, FALSE);
dnf_context_set_check_transaction(self->dnfctx, FALSE);
dnf_context_set_plugins_dir(self->dnfctx, NULL); // No plugins
Package Management Integration
1. DnfSack: Package Database
rpm-ostree uses libdnf's DnfSack as the primary package database, but with custom loading patterns:
// Custom sack loading for OSTree roots
static gboolean get_sack_for_root(int dfd, const char *path, DnfSack **out_sack, GError **error) {
g_autoptr(DnfSack) sack = dnf_sack_new();
dnf_sack_set_rootdir(sack, fullpath);
if (!dnf_sack_setup(sack, 0, error))
return FALSE;
if (!dnf_sack_load_system_repo(sack, NULL, 0, error))
return FALSE;
*out_sack = util::move_nullify(sack);
return TRUE;
}
2. Package Querying and Resolution
rpm-ostree extensively uses libdnf's query system with custom patterns:
// Package matching with HyQuery
g_autoptr(GPtrArray) matches = NULL;
HySelector selector = NULL;
HySubject subject = NULL;
subject = hy_subject_create(pattern);
selector = hy_subject_get_best_selector(subject, sack, NULL, FALSE, NULL);
matches = hy_selector_matches(selector);
3. Dependency Resolution with HyGoal
The core dependency resolution uses libdnf's HyGoal system:
// From rpmostree_context_prepare()
DnfSack *sack = dnf_context_get_sack(dnfctx);
HyGoal goal = dnf_context_get_goal(dnfctx);
// Lock existing packages to prevent unwanted changes
hy_autoquery HyQuery query = hy_query_create(sack);
hy_query_filter(query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME);
g_autoptr(GPtrArray) pkgs = hy_query_run(query);
for (guint i = 0; i < pkgs->len; i++) {
auto pkg = static_cast<DnfPackage *>(pkgs->pdata[i]);
if (hy_goal_lock(goal, pkg, error) != 0)
return glnx_prefix_error(error, "while locking pkg '%s'", pkgname);
}
// Perform dependency resolution
auto actions = static_cast<DnfGoalActions>(DNF_INSTALL | DNF_ALLOW_UNINSTALL);
if (!self->treefile_rs->get_recommends())
actions = static_cast<DnfGoalActions>(static_cast<int>(actions) | DNF_IGNORE_WEAK_DEPS);
if (!dnf_goal_depsolve(goal, actions, error))
return FALSE;
Repository Management
1. OSTree-Aware Repository Configuration
rpm-ostree customizes repository management to work with OSTree deployments:
void rpmostree_context_configure_from_deployment(RpmOstreeContext *self,
OstreeSysroot *sysroot,
OstreeDeployment *cfg_deployment) {
g_autofree char *cfg_deployment_root = rpmostree_get_deployment_root(sysroot, cfg_deployment);
g_autofree char *reposdir = g_build_filename(cfg_deployment_root, "etc/yum.repos.d", NULL);
// Point libdnf to the yum.repos.d of the merge deployment
rpmostree_context_set_repos_dir(self, reposdir);
// Point the core to the passwd & group of the merge deployment
self->passwd_dir = g_build_filename(cfg_deployment_root, "etc", NULL);
}
2. Package Cache Integration
rpm-ostree maintains a sophisticated package cache using OSTree repositories:
// Package cache operations
gboolean rpmostree_pkgcache_find_pkg_header(OstreeRepo *pkgcache, const char *nevra,
const char *expected_sha256, GVariant **out_header,
GCancellable *cancellable, GError **error);
// Cache branch naming convention
char *rpmostree_get_cache_branch_for_n_evr_a(const char *name, const char *evr, const char *arch);