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.
315 lines
12 KiB
C
315 lines
12 KiB
C
/**
|
|
* @file core.h
|
|
* @brief Public C API for libdpm-core
|
|
*
|
|
* The single entry point for all DPM consumers: the dpm CLI, modules,
|
|
* and external programs (build systems, Dark Horse tooling). All types
|
|
* crossing this boundary are C types; state passes through opaque
|
|
* handles; errors are int codes with per-context detail strings.
|
|
*
|
|
* @copyright Copyright (c) 2026 SILO GROUP LLC
|
|
* @author Chris Punches <chris.punches@silogroup.org>
|
|
*
|
|
* Part of the Dark Horse Linux Package Manager (DPM)
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef DPM_CORE_H
|
|
#define DPM_CORE_H
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Export annotation */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* Marks the public API visible. The library is compiled with hidden
|
|
* default symbol visibility; these functions are its entire exported
|
|
* surface.
|
|
*/
|
|
#ifndef DPM_API
|
|
#define DPM_API __attribute__((visibility("default")))
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Opaque handles */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
typedef struct dpm_ctx dpm_ctx;
|
|
typedef struct dpm_module dpm_module;
|
|
typedef struct dpm_cursor dpm_cursor;
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Log levels */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
enum {
|
|
DPM_LOG_FATAL = 0,
|
|
DPM_LOG_ERROR = 1,
|
|
DPM_LOG_WARN = 2,
|
|
DPM_LOG_INFO = 3,
|
|
DPM_LOG_DEBUG = 4
|
|
};
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Context configuration overrides */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* Overrides for dpm_open(). Any field may be left NULL (or -1 for
|
|
* log_level) to accept configuration-file values and built-in defaults.
|
|
* Every field here is exposed as a dpm CLI flag.
|
|
*/
|
|
typedef struct dpm_open_overrides {
|
|
const char* config_dir; /* NULL = /etc/dpm/conf.d/ */
|
|
const char* module_path; /* NULL = config value, then built-in */
|
|
const char* root; /* NULL = "/" (target root for pkg ops) */
|
|
int log_level; /* -1 = config value; else DPM_LOG_* */
|
|
} dpm_open_overrides;
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Module information (enumeration results) */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
typedef struct dpm_module_info {
|
|
const char* name; /* module name (filename minus .so) */
|
|
const char* version; /* module's own X.Y.Z */
|
|
const char* description; /* one-line description */
|
|
} dpm_module_info;
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Context lifecycle */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* @brief Creates a libdpm-core context
|
|
*
|
|
* Reads configuration from /etc/dpm/conf.d/ (or the overridden config
|
|
* directory), resolves the module path (override > config > built-in
|
|
* default), and initializes logging per configuration. Performs no
|
|
* module loading. Multiple simultaneous contexts with different roots
|
|
* are legal.
|
|
*
|
|
* @param overrides Optional overrides; NULL accepts configuration
|
|
* values and built-in defaults
|
|
* @return A context handle, or NULL on allocation failure or an
|
|
* unreadable/invalid explicit override (a missing default
|
|
* config directory is not an error)
|
|
*/
|
|
DPM_API dpm_ctx* dpm_open(const dpm_open_overrides* overrides);
|
|
|
|
/**
|
|
* @brief Releases a libdpm-core context
|
|
*
|
|
* Unloads every module handle the context issued, closes log targets,
|
|
* and frees all memory owned by the context. All handles and strings
|
|
* obtained through the context are invalid after this call.
|
|
*
|
|
* @param ctx The context to release; NULL is a no-op
|
|
*/
|
|
DPM_API void dpm_close(dpm_ctx* ctx);
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Module acquisition */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* @brief Loads and returns a validated module
|
|
*
|
|
* Resolves the named module in the module path and runs the full
|
|
* load-time validation sequence if it is not already loaded in this
|
|
* context. Modules are loaded at most once per context; repeated
|
|
* 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
|
|
* acceptable.
|
|
*
|
|
* @param ctx The libdpm-core context
|
|
* @param name The module name (its filename minus .so)
|
|
* @return A module handle owned by the context, or NULL on failure
|
|
* with the precise reason retrievable via dpm_last_error()
|
|
*/
|
|
DPM_API dpm_module* dpm_require(dpm_ctx* ctx, const char* name);
|
|
|
|
/**
|
|
* @brief Reports what the library sees in a loaded module
|
|
*
|
|
* Fills `out` with the module's name, version, and description,
|
|
* exactly as they were read at load. The caller decides whether the
|
|
* version it is looking at suits its purposes.
|
|
*
|
|
* @param ctx The libdpm-core context
|
|
* @param mod A module handle from dpm_require()
|
|
* @param out Receives the module's information; the string pointers
|
|
* remain valid until context close
|
|
* @return 0 on success, nonzero if the module cannot be reported on
|
|
*/
|
|
DPM_API int dpm_module_info_of(dpm_ctx* ctx, dpm_module* mod,
|
|
dpm_module_info* out);
|
|
|
|
/**
|
|
* @brief Dispatches a command to a module
|
|
*
|
|
* Invokes the module's dpm_module_execute with the context, the
|
|
* command, and the argument vector. argv[0] is the command when
|
|
* argc > 0; semantics beyond that are the module's to define.
|
|
*
|
|
* This is the only path into module code. A caller addresses a module
|
|
* by name and a capability by command string, so it holds no
|
|
* compile-time knowledge of the module it is calling — the same call
|
|
* a module makes to reach a peer.
|
|
*
|
|
* @param ctx The libdpm-core context
|
|
* @param mod A module handle from dpm_require()
|
|
* @param command The command name; NULL or empty behaves as the
|
|
* module's help command
|
|
* @param argc Number of arguments
|
|
* @param argv Argument vector
|
|
* @return The module's return value verbatim; 0 on success
|
|
*/
|
|
DPM_API int dpm_execute(dpm_ctx* ctx, dpm_module* mod, const char* command,
|
|
int argc, char** argv);
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Enumeration */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* @brief Enumerates the valid modules in the module path
|
|
*
|
|
* Scans the module path and validates each candidate .so; failures
|
|
* are logged and excluded from the results.
|
|
*
|
|
* @param ctx The libdpm-core context
|
|
* @return A cursor over all valid modules, or NULL on an unreadable
|
|
* module path
|
|
*/
|
|
DPM_API dpm_cursor* dpm_list_modules(dpm_ctx* ctx);
|
|
|
|
/**
|
|
* @brief Advances an enumeration cursor
|
|
*
|
|
* Fills `out` with the next module's name, version, and description;
|
|
* the string pointers remain valid until context close.
|
|
*
|
|
* @param cur The cursor from dpm_list_modules()
|
|
* @param out Receives the next module's information
|
|
* @return 0 while entries remain; nonzero at end
|
|
*/
|
|
DPM_API int dpm_cursor_next(dpm_cursor* cur, dpm_module_info* out);
|
|
|
|
/**
|
|
* @brief Releases an enumeration cursor
|
|
*
|
|
* @param cur The cursor to release; NULL is a no-op
|
|
*/
|
|
DPM_API void dpm_cursor_free(dpm_cursor* cur);
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Services (available to modules and external consumers alike) */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* @brief Returns libdpm-core's own version
|
|
*
|
|
* @return The libdpm-core version as a static X.Y.Z string; callable
|
|
* without a context
|
|
*/
|
|
DPM_API const char* dpm_core_version(void);
|
|
|
|
/**
|
|
* @brief Returns a configuration value from a module's namespace
|
|
*
|
|
* A module's configuration namespace is its own .conf file under the
|
|
* context's configuration directory; the namespace named "core", from
|
|
* core.conf, is the library's own.
|
|
*
|
|
* @param ctx The libdpm-core context
|
|
* @param module The configuration namespace to read
|
|
* @param section The section name within the file
|
|
* @param key The key within the section
|
|
* @return The configured value, or NULL if unset; valid until
|
|
* context close
|
|
*/
|
|
DPM_API const char* dpm_config_get(dpm_ctx* ctx, const char* module,
|
|
const char* section, const char* key);
|
|
|
|
/**
|
|
* @brief Writes a message to the context's configured log targets
|
|
*
|
|
* Targets are the console and, when configured, the log file.
|
|
* Messages above the configured level are dropped.
|
|
*
|
|
* @param ctx The libdpm-core context
|
|
* @param level The severity (DPM_LOG_FATAL through DPM_LOG_DEBUG)
|
|
* @param message The message to log; NULL is a no-op
|
|
*/
|
|
DPM_API void dpm_log(dpm_ctx* ctx, int level, const char* message);
|
|
|
|
/**
|
|
* @brief Returns the resolved module directory path
|
|
*
|
|
* @param ctx The libdpm-core context
|
|
* @return The module directory path this context resolved
|
|
*/
|
|
DPM_API const char* dpm_module_path(dpm_ctx* ctx);
|
|
|
|
/**
|
|
* @brief Returns the most recent failure recorded on the context
|
|
*
|
|
* @param ctx The libdpm-core context
|
|
* @return A human-readable description of the most recent failure, or
|
|
* NULL if none; overwritten by the next failing call
|
|
*/
|
|
DPM_API const char* dpm_last_error(dpm_ctx* ctx);
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Module contract (implemented by modules, called by libdpm-core) */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/*
|
|
* Every module exports, as extern "C":
|
|
*
|
|
* int dpm_module_execute(dpm_ctx* ctx, const char* command,
|
|
* int argc, char** argv);
|
|
* const char* dpm_module_version(void);
|
|
* const char* dpm_module_description(void);
|
|
*
|
|
* dpm_module_execute is the module's entire functional surface; its
|
|
* capabilities are addressed by command string, so a module publishes
|
|
* no headers, struct layouts, or symbols to anything that calls it.
|
|
*
|
|
* A module determines for itself whether it can work with the library
|
|
* it is running against: dpm_core_version() reports the running
|
|
* version, and the module proceeds or fails on its own judgement. A
|
|
* module is built against the system-installed libdpm-core.so and is
|
|
* responsible for being correct against it.
|
|
*
|
|
* The library refuses to load any module that does not validate
|
|
* completely (see the DPM specification: load-time enforcement).
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* DPM_CORE_H */
|