Module records and aliases

Installation records what a module reports about itself. dpm_install_module
opens a module once, writes its version and description to a .meta file in
/var/lib/dpm/metadata/, and records the alternate names it declared in
modules.aliases beside it. dpm_uninstall_module removes both, leaving the
module file in place.

dpm_list_modules reads those records and opens no module. A module with no
record lists with a version of <uninstalled> and still loads when a caller
names it. An unreadable or absent metadata directory costs the listing its
detail and costs nothing else.

Aliases give a module alternate names, declared through the new
dpm_module_aliases contract symbol or added with dpm_add_module_alias. A
name is recorded once: one already serving as an alias, or belonging to an
installed module, is refused rather than repointed. dpm_require matches a
name against the installed modules, then the alias table, then the module
path.

The metadata directory is a fifth override field and the -M flag, and
[modules] metadata in core.conf.

The test suite is four binaries covering context, modules, records, and
aliases, each a ctest case of its own, alongside the CLI cases.
This commit is contained in:
2026-08-24 03:51:39 -04:00
parent 71d409b5c7
commit 0291e61fd8
32 changed files with 2147 additions and 290 deletions

View File

@@ -37,12 +37,13 @@ dpm_open_overrides ov = {
"/path/to/conf.d",
"/path/to/modules",
"/path/to/root",
-1
-1,
"/path/to/metadata"
};
dpm_ctx* ctx = dpm_open(&ov);
```
Leaving `config_dir` NULL selects `/etc/dpm/conf.d/`; leaving `module_path` NULL selects the configured value and then the built-in default; leaving `root` NULL selects `/`; a `log_level` of -1 takes the configured value.
Leaving `config_dir` NULL selects `/etc/dpm/conf.d/`; leaving `module_path` NULL selects the configured value and then the built-in default; leaving `root` NULL selects `/`; a `log_level` of -1 takes the configured value; leaving `metadata_dir` NULL selects the configured value and then `/var/lib/dpm/metadata/`.
The root override is what makes chroot builds, image assembly, and sysroot management work: package operations act on the given tree instead of the running system. Multiple simultaneous contexts with different roots are legal.
@@ -56,6 +57,8 @@ dpm_module* mod = dpm_require(ctx, "mymodule");
libdpm-core.so validates the module completely at load; a handle is returned only for a fully valid module. NULL means the module is absent or invalid — `dpm_get_last_error`(ctx) carries the precise reason. Modules load at most once per context; repeated calls return the same handle.
The name is matched against the installed modules first, then the alias table, then the module path itself, so a module answers to its own name and to any alias recorded for it. A name that reaches the module path belongs to a module nobody has installed; that is logged and the module loads anyway.
`dpm_get_module_info` reports what the library saw in the loaded module:
```
@@ -86,17 +89,23 @@ while (dpm_cursor_next(cur, &info) == 0) {
dpm_cursor_free(cur);
```
The cursor covers every valid module in the module path; invalid candidates are excluded and logged.
The cursor covers every module in the module path, reported from what installation recorded. An installed module carries its recorded version and description; one with no record carries a version of `<uninstalled>`. No module is opened, so enumerating executes nothing.
## Records and Aliases
`dpm_install_module` loads a module once, writes down what it reports, and records the alternate names it declared. `dpm_uninstall_module` removes both, leaving the module file in place. `dpm_add_module_alias`, `dpm_remove_module_alias`, and `dpm_list_module_aliases` manage alternate names directly; the alias cursor is advanced with `dpm_alias_cursor_next` and released with `dpm_alias_cursor_free`.
A name is recorded once. Adding one that is already an alias, or that belongs to an installed module, is refused rather than repointed.
## Services
- `dpm_core_version`()** — the library version; callable without a context.
- `dpm_get_module_info`(ctx, mod, out)** — the name, version, and description read from a loaded module.
- `dpm_config_get`(ctx, module, section, key)** — a value from a module's configuration namespace, or NULL if unset.
- `dpm_log`(ctx, level, message)** — writes to the context's configured log targets; levels are `DPM_LOG_FATAL` through `DPM_LOG_DEBUG`.
- `dpm_get_resolved_module_path`(ctx)** — the resolved module directory.
- `dpm_core_version`() — the library version; callable without a context.
- `dpm_get_module_info`(ctx, mod, out) — the name, version, and description read from a loaded module.
- `dpm_config_get`(ctx, module, section, key) — a value from a module's configuration namespace, or NULL if unset.
- `dpm_log`(ctx, level, message) — writes to the context's configured log targets; levels are `DPM_LOG_FATAL` through `DPM_LOG_DEBUG`.
- `dpm_get_resolved_module_path`(ctx) — the resolved module directory.
- `dpm_set_last_error`(ctx, msg) — records a failure reason on the context; what a module calls to explain a nonzero return.
- `dpm_get_last_error`(ctx)** — a human-readable description of the most recent failure on the context, or NULL.
- `dpm_get_last_error`(ctx) — a human-readable description of the most recent failure on the context, or NULL.
## Ownership and Errors