199 lines
No EOL
6.9 KiB
Markdown
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 |