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

199 lines
No EOL
6.9 KiB
Markdown

# 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:
```c
// 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:
```c
// 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:
```c
// 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`:
```cpp
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**
```cpp
// 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:
```cpp
// 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:
```cpp
// 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:
```cpp
// 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:
```cpp
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:
```cpp
// 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