dpm_execute fills a dpm_result on every call: status and error_code from the module's return value, and a payload the module hands back through dpm_set_result. The payload's layout belongs to the module and is documented per command; its release function is carried in the envelope and invoked by dpm_result_release. The context keeps a stack of the envelopes in progress, so a module calling a peer receives the peer's payload in its own envelope and its caller sees only what the module sets itself. The good fixture returns a payload and counts its releases, and the modules test covers the envelope fields, the payload round trip, single release, discard on a NULL envelope, and a payload set outside any dispatch. The prose documents describe the envelope and the payload header a module ships.
7.7 KiB
Interaction with libdpm-core.so
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, arguments, and an envelope for the result:
dpm_result result;
int rc = dpm_execute(ctx, mod, "command", argc, argv, &result);
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. What a module accepts as commands and arguments, what its return codes mean, and what it returns as a payload, is documented by that module.
Results
Every dispatch fills the envelope. result.status is DPM_RESULT_OK when the module returned 0 and DPM_RESULT_ERROR otherwise; result.error_code is the module's return value. Those two fields mean the same thing for every module and every command.
result.data is the payload: what the module returns for that command, in a layout the module documents for that command at that version. The module ships that layout as a header from its own repository, and your program includes it for the commands it reads. A module that returns nothing leaves data NULL.
The payload belongs to the module that produced it. When you are done with it:
dpm_result_release(&result);
That calls the release function the module supplied and clears data. Passing NULL as the envelope to dpm_execute discards the payload before the call returns, for a command whose result you do not need.
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 areDPM_LOG_FATALthroughDPM_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_set_result(ctx, data, release) — hands a payload to the dispatch in progress; what a module calls to return data.dpm_result_release(result) — releases a payload through the function the module supplied.
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. A payload in a dpm_result is owned by the module that produced it and is released with dpm_result_release. 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;
}
dpm_result result;
int rc = dpm_execute(ctx, mod, "version", 0, NULL, &result);
dpm_result_release(&result);
dpm_close(ctx);
return rc;
}