/** * @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 * * 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 . */ #ifndef DPM_CORE_H #define DPM_CORE_H #include #include #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 */ const char* core_min; /* minimum libdpm-core version it supports */ } dpm_module_info; /* ------------------------------------------------------------------ */ /* Module contract structures (layout fixed by the DPM spec) */ /* ------------------------------------------------------------------ */ /** Magic constant opening every module API table. */ #define DPM_API_TABLE_MAGIC 0x314D5044u /* "DPM1" */ /** * Every module API table begins with this header: the magic constant, * then the full size in bytes of the table struct as the exporting * module compiled it (permits tail-extension within a table version). */ typedef struct dpm_api_table_header { uint32_t magic; uint32_t size; } dpm_api_table_header; /** One entry of a module's declared functional surface. */ typedef struct dpm_manifest_entry { const char* api_name; /* e.g. "raw" */ int table_version; /* e.g. 1 */ const char* symbol; /* exact exported symbol carrying the table, e.g. "raw_api_v1" */ } dpm_manifest_entry; /** A module's manifest: its entire declared functional surface. */ typedef struct dpm_manifest { uint32_t count; const dpm_manifest_entry* entries; } dpm_manifest; /* ------------------------------------------------------------------ */ /* 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, not this library's: * 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 libdpm-core sees in a loaded module * * Fills `out` with the module's name, version, description, and * minimum-libdpm-core version, exactly as they were read at load. No * compatibility conclusion is drawn from these values; the caller * decides whether the version it is looking at is too new or too * old for 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 Returns a module's API table for direct typed calls * * The table was already validated (manifest cross-check, magic, * minimum size) at module load. The caller casts the pointer to the * table struct type for that API and version. The table is valid for * the life of the context. * * @param ctx The libdpm-core context * @param mod A module handle from dpm_require() * @param api_name The API name as declared in the module's manifest * @param table_version The table version to retrieve * @return The table pointer, or NULL if the module does not provide * that api/version pair */ DPM_API const void* dpm_get_api(dpm_ctx* ctx, dpm_module* mod, const char* api_name, int table_version); /** * @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. * * @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, description, and * minimum-libdpm-core version; 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); * const char* dpm_module_core_min(void); * const dpm_manifest* dpm_module_manifest(void); * * plus one exported table symbol per manifest entry. libdpm-core * refuses to * load any module that does not validate completely (see the DPM * specification: load-time enforcement). */ #ifdef __cplusplus } #endif #endif /* DPM_CORE_H */