Modules determine their own compatibility with the library
A module is built against the system-installed libdpm-core.so and is responsible for being correct against it. Where it needs to act on the version it is running under, dpm_core_version() reports that and the module decides for itself. dpm_module_core_min() is removed. It was a declaration handed to the library to enforce on the module's behalf, and enforcement of that kind belongs nowhere in a library that routes and hosts. The contract is now three reserved symbols and load validation is two steps: the reserved symbols resolve, and the version and description probes return well-formed values. compare_versions had no remaining caller and is removed; parse_version stays for the well-formedness probe. The core_too_new fixture went with the handshake it existed to exercise.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Bundles with the dpm binary and libdpm-core.so; used for testing and
|
||||
* reporting functionality of libdpm-core.so. Implements the full DPM
|
||||
* module contract: the four reserved symbols, with every capability
|
||||
* module contract: the three reserved symbols, with every capability
|
||||
* reached by command string through the entry point.
|
||||
*
|
||||
* @copyright Copyright (c) 2026 SILO GROUP LLC
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "commands.hpp"
|
||||
|
||||
#define INFO_MODULE_VERSION "0.1.0"
|
||||
#define INFO_CORE_MIN "0.1.0"
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Reserved contract symbols */
|
||||
@@ -50,14 +49,6 @@ extern "C" const char* dpm_module_description(void)
|
||||
return "Reports and tests libdpm-core functionality.";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the minimum libdpm-core version this module supports
|
||||
*/
|
||||
extern "C" const char* dpm_module_core_min(void)
|
||||
{
|
||||
return INFO_CORE_MIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Command entry point
|
||||
*
|
||||
|
||||
@@ -1,18 +1,46 @@
|
||||
/*
|
||||
* libdpm-core.map — the export list for libdpm-core.so
|
||||
* libdpm-core.map — the linker version script for libdpm-core.so
|
||||
*
|
||||
* Passed to the linker with --version-script. Names under `global` are
|
||||
* exported; `local: *` hides everything else, so the public C API is the
|
||||
* only surface a consumer or a loaded module can bind to.
|
||||
* Passed to the linker with --version-script; see CMakeLists.txt, which
|
||||
* also lists it as a link dependency so edits force a relink. It does
|
||||
* two separate jobs.
|
||||
*
|
||||
* `global` is every function in include/dpm/core.h and nothing else. A
|
||||
* new public function is added here in the commit that adds it to the
|
||||
* header.
|
||||
*
|
||||
* The node name stamps each symbol (dpm_open@@DPM_CORE_1.0), so a break
|
||||
* can ship as a new node while already-linked binaries keep resolving
|
||||
* the old one. Newest node first. DPM_CORE_0.1 was never released; it is
|
||||
* the shape a retired node takes.
|
||||
* 1. It limits the export set.
|
||||
*
|
||||
* `global` names every function declared in <dpm/core.h>. `local: *`
|
||||
* hides everything else, including the template instantiations that
|
||||
* libstdc++ headers emit with default visibility.
|
||||
*
|
||||
*
|
||||
* 2. It versions the exports.
|
||||
*
|
||||
* Every symbol here is stamped with the node name: dpm_open becomes
|
||||
* dpm_open@@DPM_CORE_1.0. The linker reads that node off the library a
|
||||
* consumer links against and records it in the consumer's own binary,
|
||||
* and at startup the dynamic linker verifies the library still provides
|
||||
* it.
|
||||
*
|
||||
* That keeps an already-installed module working after the library
|
||||
* changes underneath it, which is what makes updating libdpm-core.so
|
||||
* safe mid-bootstrap.
|
||||
*
|
||||
* A breaking change ships as a new node while the old node keeps its
|
||||
* original definitions, so binaries built against the old node keep
|
||||
* resolving them. Both live in one .so under one soname, preserving the
|
||||
* invariant the routing model depends on: exactly one instance of the
|
||||
* library mapped per process.
|
||||
*
|
||||
*
|
||||
* Changing this file:
|
||||
*
|
||||
* - A new public function goes into `global` in the same commit that
|
||||
* adds it to <dpm/core.h>.
|
||||
* - A breaking change to an existing function adds a new node above
|
||||
* DPM_CORE_1.0, leaving this node and its definitions intact.
|
||||
*
|
||||
* Newest node first. DPM_CORE_0.1 was never released; it is here as the
|
||||
* shape a retired node takes.
|
||||
*/
|
||||
DPM_CORE_1.0 {
|
||||
global:
|
||||
|
||||
@@ -96,7 +96,6 @@ std::unique_ptr<dpm_module> validate_and_load(dpm_ctx* ctx,
|
||||
"dpm_module_execute",
|
||||
"dpm_module_version",
|
||||
"dpm_module_description",
|
||||
"dpm_module_core_min",
|
||||
};
|
||||
|
||||
std::string missing;
|
||||
@@ -114,28 +113,12 @@ std::unique_ptr<dpm_module> validate_and_load(dpm_ctx* ctx,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto exec_f = reinterpret_cast<execute_fn>(resolve(handle, "dpm_module_execute"));
|
||||
auto version_f = reinterpret_cast<string_fn>(resolve(handle, "dpm_module_version"));
|
||||
auto desc_f = reinterpret_cast<string_fn>(resolve(handle, "dpm_module_description"));
|
||||
auto core_min_f = reinterpret_cast<string_fn>(resolve(handle, "dpm_module_core_min"));
|
||||
auto exec_f = reinterpret_cast<execute_fn>(resolve(handle, "dpm_module_execute"));
|
||||
auto version_f = reinterpret_cast<string_fn>(resolve(handle, "dpm_module_version"));
|
||||
auto desc_f = reinterpret_cast<string_fn>(resolve(handle, "dpm_module_description"));
|
||||
|
||||
/* Step 2: minimum-version handshake. */
|
||||
const char* core_min = core_min_f();
|
||||
/* Step 2: probe the cheap calls. */
|
||||
long parsed[3];
|
||||
if (!core_min || !parse_version(core_min, parsed)) {
|
||||
reason = "dpm_module_core_min() returned a malformed version";
|
||||
dlclose(handle);
|
||||
return nullptr;
|
||||
}
|
||||
if (compare_versions(core_min, DPM_CORE_VERSION_STR) > 0) {
|
||||
reason = std::string("requires libdpm-core >= ") + core_min +
|
||||
", running libdpm-core is " DPM_CORE_VERSION_STR
|
||||
" — update libdpm-core";
|
||||
dlclose(handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Step 3: probe the cheap calls. */
|
||||
const char* version = version_f();
|
||||
if (!version || !parse_version(version, parsed)) {
|
||||
reason = "dpm_module_version() returned a malformed version";
|
||||
@@ -154,7 +137,6 @@ std::unique_ptr<dpm_module> validate_and_load(dpm_ctx* ctx,
|
||||
mod->handle = handle;
|
||||
mod->version = version;
|
||||
mod->description = description;
|
||||
mod->core_min = core_min;
|
||||
mod->execute = exec_f;
|
||||
return mod;
|
||||
}
|
||||
@@ -196,7 +178,6 @@ int dpm_module_info_of(dpm_ctx* ctx, dpm_module* mod, dpm_module_info* out)
|
||||
out->name = mod->name.c_str();
|
||||
out->version = mod->version.c_str();
|
||||
out->description = mod->description.c_str();
|
||||
out->core_min = mod->core_min.c_str();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -264,7 +245,6 @@ dpm_cursor* dpm_list_modules(dpm_ctx* ctx)
|
||||
info.name = mod->name.c_str();
|
||||
info.version = mod->version.c_str();
|
||||
info.description = mod->description.c_str();
|
||||
info.core_min = mod->core_min.c_str();
|
||||
cur->infos.push_back(info);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file version.cpp
|
||||
* @brief X.Y.Z version parsing and comparison
|
||||
* @brief X.Y.Z version parsing
|
||||
*
|
||||
* @copyright Copyright (c) 2026 SILO GROUP LLC
|
||||
* @author Chris Punches <chris.punches@silogroup.org>
|
||||
@@ -60,24 +60,4 @@ bool parse_version(const char* s, long out[3])
|
||||
return true;
|
||||
}
|
||||
|
||||
int compare_versions(const char* a, const char* b)
|
||||
{
|
||||
long va[3] = {0, 0, 0};
|
||||
long vb[3] = {0, 0, 0};
|
||||
|
||||
parse_version(a, va);
|
||||
parse_version(b, vb);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (va[i] < vb[i]) {
|
||||
return -1;
|
||||
}
|
||||
if (va[i] > vb[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace dpmcore
|
||||
|
||||
Reference in New Issue
Block a user