From 71d409b5c7e64adaef28470c0da9fa061b1c3328 Mon Sep 17 00:00:00 2001 From: "Christopher M. Punches" Date: Wed, 19 Aug 2026 02:34:28 -0400 Subject: [PATCH] One way to record an error, and a name that reads as a query dpm_module_info_of becomes dpm_get_module_info, joining the other two readers on the context in stating that a call fetches a value. Recording an error had two entry points doing the same write: the exported dpm_set_last_error and an internal dpm_core::set_last_error taking a std::string. The internal one is gone and the loader calls the exported function, so a reason reaches the context by one path whether a module or the library records it. --- docs/PROSE/CONSUMERS.md | 6 +++--- docs/PROSE/DESIGN.md | 2 +- docs/PROSE/MODULES.md | 2 +- docs/PROSE/OVERVIEW.md | 2 +- include/dpm/core.h | 10 +++++----- include/internal/context.hpp | 13 ++----------- src/core/context.cpp | 14 -------------- src/core/modules.cpp | 10 +++++----- tests/test_core.cpp | 6 +++--- 9 files changed, 21 insertions(+), 44 deletions(-) diff --git a/docs/PROSE/CONSUMERS.md b/docs/PROSE/CONSUMERS.md index b35485a..ec7c718 100644 --- a/docs/PROSE/CONSUMERS.md +++ b/docs/PROSE/CONSUMERS.md @@ -56,11 +56,11 @@ 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. -`dpm_module_info_of` reports what the library saw in the loaded module: +`dpm_get_module_info` reports what the library saw in the loaded module: ``` dpm_module_info info; -dpm_module_info_of(ctx, mod, &info); +dpm_get_module_info(ctx, mod, &info); ``` `info` carries `name`, `version`, and `description`. @@ -91,7 +91,7 @@ The cursor covers every valid module in the module path; invalid candidates are ## Services - `dpm_core_version`()** — the library version; callable without a context. -- `dpm_module_info_of`(ctx, mod, out)** — the name, version, and description read from a loaded module. +- `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. diff --git a/docs/PROSE/DESIGN.md b/docs/PROSE/DESIGN.md index 38fd4be..f5d5238 100644 --- a/docs/PROSE/DESIGN.md +++ b/docs/PROSE/DESIGN.md @@ -62,7 +62,7 @@ Releases the context: unloads every module handle it issued, closes log targets, `dpm_module* dpm_require(dpm_ctx* ctx, const char* name)` Resolves the module `name` in the module path and runs the full load-time validation sequence (see Load-Time Enforcement) if the module is not already loaded in this context. On success returns a module handle owned by the context (repeated calls return the same handle — modules are loaded at most once per context). On failure returns NULL and records the precise reason: not found, or validation step failed with the step and detail. No version criterion is applied here; compatibility is the caller's to determine from the reported version. -`int dpm_module_info_of(dpm_ctx* ctx, dpm_module* mod, dpm_module_info* out)` +`int dpm_get_module_info(dpm_ctx* ctx, dpm_module* mod, dpm_module_info* out)` Fills `out` with the loaded module's name, version, and description exactly as they were read at load (string pointers valid until context close). This is how a consumer obtains the version it will judge. The library attaches no meaning to the values. Returns 0 on success, nonzero if the module cannot be reported on. `int dpm_execute(dpm_ctx* ctx, dpm_module* mod, const char* command, int argc, char** argv)` diff --git a/docs/PROSE/MODULES.md b/docs/PROSE/MODULES.md index c2f1768..1fb6aba 100644 --- a/docs/PROSE/MODULES.md +++ b/docs/PROSE/MODULES.md @@ -60,7 +60,7 @@ The `ctx` is the one handed to your entry point. Nothing else is needed to reach **Never link, include, or hardcode anything belonging to a peer.** No peer headers, no shared types, no peer symbols. Modules are loaded with `RTLD_LOCAL`, so a peer's symbols are not reachable from your module even if you tried — libdpm-core.so is the only path, and the only knowledge you hold about a peer is its name and the commands it documents. -A module that depends on a peer is the party that judges the peer's version. Require it, read its reported version with `dpm_module_info_of`, and decide whether it is suitable for the commands you intend to issue. libdpm-core.so reports; it does not rule. +A module that depends on a peer is the party that judges the peer's version. Require it, read its reported version with `dpm_get_module_info`, and decide whether it is suitable for the commands you intend to issue. libdpm-core.so reports; it does not rule. ## Validation at Load diff --git a/docs/PROSE/OVERVIEW.md b/docs/PROSE/OVERVIEW.md index bc56e36..307122c 100644 --- a/docs/PROSE/OVERVIEW.md +++ b/docs/PROSE/OVERVIEW.md @@ -53,7 +53,7 @@ The context owns everything it hands out. Every string a caller receives stays v **Acquiring a module.** `dpm_require` resolves a module by name, validates it completely, and returns a handle — or returns NULL, with `dpm_get_last_error` carrying the precise reason. Modules load at most once per context, and repeated calls return the same handle. -**Reading what the library saw.** `dpm_module_info_of` fills in a module's name, version, and description. Those values are reported, and no conclusion is drawn from them. +**Reading what the library saw.** `dpm_get_module_info` fills in a module's name, version, and description. Those values are reported, and no conclusion is drawn from them. **Calling into a module.** `dpm_execute` passes a command name and an argument vector to the module's entry point and returns its result. This is the only path into module code, and it is the same one the `dpm` binary takes. diff --git a/include/dpm/core.h b/include/dpm/core.h index f694ec7..92b9abb 100644 --- a/include/dpm/core.h +++ b/include/dpm/core.h @@ -154,7 +154,7 @@ typedef struct dpm_open_overrides { /** * @brief What the library read from a module at load * - * Filled by dpm_module_info_of() and by dpm_cursor_next(). The string + * Filled by dpm_get_module_info() and by dpm_cursor_next(). The string * pointers remain valid until the context is closed. */ typedef struct dpm_module_info { @@ -215,7 +215,7 @@ void dpm_close(dpm_ctx* ctx); * calls return the same handle. * * Version compatibility is the caller's judgement: read the loaded - * module's version with dpm_module_info_of() and decide whether it is + * module's version with dpm_get_module_info() and decide whether it is * acceptable. * * @param ctx The libdpm-core.so context @@ -240,8 +240,8 @@ dpm_module* dpm_require(dpm_ctx* ctx, const char* name); * @return 0 on success, nonzero if the module cannot be reported on */ DPM_PUBLIC_ABI_EXPORT -int dpm_module_info_of(dpm_ctx* ctx, dpm_module* mod, - dpm_module_info* out); +int dpm_get_module_info(dpm_ctx* ctx, dpm_module* mod, + dpm_module_info* out); /** * @brief Dispatches a command to a module @@ -399,7 +399,7 @@ const char* dpm_get_last_error(dpm_ctx* ctx); * * dpm_module_execute is the only entry through which a module performs * work. The other two are what it reports about itself; the library - * reads both at load and serves them through dpm_module_info_of. + * reads both at load and serves them through dpm_get_module_info. */ #ifdef __cplusplus diff --git a/include/internal/context.hpp b/include/internal/context.hpp index 301e412..a15621d 100644 --- a/include/internal/context.hpp +++ b/include/internal/context.hpp @@ -67,8 +67,8 @@ struct dpm_ctx { * @brief Internal implementation of libdpm-core.so * * Holds what the library's translation units share with each other and - * with nothing else: the loader, the configuration reader, the error - * recorder, and the version parser. It is declared in include/internal/, + * with nothing else: the loader, the configuration reader, the value + * sanitizers, and the version parser. It is declared in include/internal/, * which is never installed, so a consumer or a module cannot name any of * it. * @@ -82,12 +82,3 @@ struct dpm_ctx { * unnamed namespace instead, which gives them internal linkage and keeps * them from colliding across files. */ -namespace dpm_core { - /** - * @brief Records a failure reason on the context - * - * @param ctx The libdpm-core.so context; NULL is a no-op - * @param msg The failure description - */ - void set_last_error(dpm_ctx* ctx, const std::string& msg); -} // namespace dpm_core diff --git a/src/core/context.cpp b/src/core/context.cpp index 4b93fb5..90a3bec 100644 --- a/src/core/context.cpp +++ b/src/core/context.cpp @@ -49,20 +49,6 @@ namespace { const char* DEFAULT_LOG_FILE = "/var/log/dpm/dpm.log"; } // namespace -namespace dpm_core { - /** - * @brief Records a failure reason on the context - * - * Overwrites whatever reason was recorded previously, so the context - * carries the most recent failure and nothing older. - */ - void set_last_error(dpm_ctx* ctx, const std::string& msg) { - if (ctx) { - ctx->last_error = msg; - } - } -} // namespace dpm_core - extern "C" { /** * @brief Creates a context diff --git a/src/core/modules.cpp b/src/core/modules.cpp index c41241a..30f6e27 100644 --- a/src/core/modules.cpp +++ b/src/core/modules.cpp @@ -213,8 +213,8 @@ extern "C" { std::string reason; auto loaded = dpm_core::validate_and_load(ctx, name, reason); if (!loaded) { - dpm_core::set_last_error(ctx, std::string("module '") + name + - "': " + reason); + dpm_set_last_error(ctx, (std::string("module '") + name + + "': " + reason).c_str()); return nullptr; } @@ -231,7 +231,7 @@ extern "C" { * The strings point into the module's registry entry, so they remain * valid as long as the context holds the module. */ - int dpm_module_info_of(dpm_ctx* ctx, dpm_module* mod, dpm_module_info* out) { + int dpm_get_module_info(dpm_ctx* ctx, dpm_module* mod, dpm_module_info* out) { if (!ctx || !mod || !out) { return 1; } @@ -275,8 +275,8 @@ extern "C" { std::error_code ec; if (!fs::is_directory(ctx->module_path, ec)) { - dpm_core::set_last_error(ctx, "module path is not a readable directory: " + - ctx->module_path); + dpm_set_last_error(ctx, ("module path is not a readable directory: " + + ctx->module_path).c_str()); return nullptr; } diff --git a/tests/test_core.cpp b/tests/test_core.cpp index 1b11926..4048e19 100644 --- a/tests/test_core.cpp +++ b/tests/test_core.cpp @@ -117,13 +117,13 @@ int main(void) { /* libdpm-core reports what it saw; the caller judges compatibility */ dpm_module_info seen; - CHECK(dpm_module_info_of(ctx, good, &seen) == 0); + CHECK(dpm_get_module_info(ctx, good, &seen) == 0); CHECK(std::strcmp(seen.name, "good") == 0); CHECK(std::strcmp(seen.version, "1.2.3") == 0); CHECK(seen.description != nullptr && *seen.description); - CHECK(dpm_module_info_of(ctx, nullptr, &seen) != 0); - CHECK(dpm_module_info_of(ctx, good, nullptr) != 0); + CHECK(dpm_get_module_info(ctx, nullptr, &seen) != 0); + CHECK(dpm_get_module_info(ctx, good, nullptr) != 0); /* dispatch: the module's return value comes back verbatim, so a command round trip is observable without any compile-time