apt-ostree/.notes/rpm-ostree/libdnf.md

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);

Multi-Language Integration Layers