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.
140 lines
6.4 KiB
Markdown
140 lines
6.4 KiB
Markdown
# Interaction with libdpm-core.so {#consumers}
|
|
|
|
Programs link libdpm-core.so as an ordinary shared library dependency — the same way they link any other library — to reach the package manager in-process: build systems, installers, image builders, system tooling, and foreign-language bindings all use the library the `dpm` binary is built on. A program holding a default context is working against system configuration, the system module path, the system tree, and system locking, the same environment the installed `dpm` binary sees.
|
|
|
|
## Compiling and Linking
|
|
|
|
With the library installed, include the public header and link it:
|
|
|
|
```
|
|
#include <dpm/core.h>
|
|
```
|
|
|
|
```
|
|
g++ myprog.cpp -ldpm-core
|
|
```
|
|
|
|
The header installs to the standard include path and the library to the standard lib path, so no additional flags are required. The interface is a C ABI: every function is extern "C", every type crossing the boundary is a C type, and state passes through opaque handles — callable from C, C++, or any language with C FFI.
|
|
|
|
`<dpm/core.h>` names no module and carries no module-specific type. It offers two things: discovery of modules, and interaction with them.
|
|
|
|
## The Context
|
|
|
|
All work happens through a context handle:
|
|
|
|
```
|
|
dpm_ctx* ctx = dpm_open(NULL);
|
|
...
|
|
dpm_close(ctx);
|
|
```
|
|
|
|
`dpm_open`(NULL) reads the system configuration (`/etc/dpm/conf.d/`), resolves the system module path, and targets the root filesystem. `dpm_close` releases every handle the context issued; all pointers obtained through the context are invalid after it.
|
|
|
|
To point a context elsewhere, pass overrides — every field is optional:
|
|
|
|
```
|
|
dpm_open_overrides ov = {
|
|
"/path/to/conf.d",
|
|
"/path/to/modules",
|
|
"/path/to/root",
|
|
-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 `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.
|
|
|
|
## Acquiring and Using Modules
|
|
|
|
`dpm_require` loads a module by name, on demand:
|
|
|
|
```
|
|
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:
|
|
|
|
```
|
|
dpm_module_info info;
|
|
dpm_get_module_info(ctx, mod, &info);
|
|
```
|
|
|
|
`info` carries `name`, `version`, and `description`.
|
|
|
|
Deciding whether that version is suitable is yours. libdpm-core.so applies no version criterion of its own — a handle means the module is valid, not that it suits you.
|
|
|
|
`dpm_execute` invokes the module — a command name and arguments:
|
|
|
|
```
|
|
int rc = dpm_execute(ctx, mod, "command", argc, argv);
|
|
```
|
|
|
|
This is the only path into module code, and it is the same path the `dpm` binary uses and the same path a module uses to reach a peer. You address a module by name and a capability by command string, so your program compiles against no module header, no struct layout, and no module symbol. What a module accepts as commands and arguments, and what its return codes mean, is documented by that module.
|
|
|
|
## Enumerating Modules
|
|
|
|
```
|
|
dpm_cursor* cur = dpm_list_modules(ctx);
|
|
dpm_module_info info;
|
|
while (dpm_cursor_next(cur, &info) == 0) {
|
|
/* each iteration fills info */
|
|
}
|
|
dpm_cursor_free(cur);
|
|
```
|
|
|
|
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_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.
|
|
|
|
## Ownership and Errors
|
|
|
|
Strings returned by the library are owned by the context (or by the module that produced them) and remain valid until `dpm_close`; callers never free them. Functions returning int use 0 for success. Functions returning pointers use NULL for failure, with detail available from `dpm_get_last_error`.
|
|
|
|
## Complete Example
|
|
|
|
```
|
|
#include <dpm/core.h>
|
|
#include <stdio.h>
|
|
|
|
int main(void) {
|
|
dpm_ctx* ctx = dpm_open(NULL);
|
|
if (!ctx) {
|
|
fprintf(stderr, "failed to initialize\n");
|
|
return 1;
|
|
}
|
|
|
|
dpm_module* mod = dpm_require(ctx, "info");
|
|
if (!mod) {
|
|
fprintf(stderr, "%s\n", dpm_get_last_error(ctx));
|
|
dpm_close(ctx);
|
|
return 1;
|
|
}
|
|
|
|
int rc = dpm_execute(ctx, mod, "version", 0, NULL);
|
|
|
|
dpm_close(ctx);
|
|
return rc;
|
|
}
|
|
```
|