mirror of
https://github.com/NixOS/nix
synced 2025-06-28 13:41:15 +02:00
C API: Keep the structure flat
See https://github.com/NixOS/nix/pull/10329
This commit is contained in:
parent
d96b52bd8b
commit
c57de60522
26 changed files with 50 additions and 32 deletions
18
src/libutil-c/local.mk
Normal file
18
src/libutil-c/local.mk
Normal file
|
@ -0,0 +1,18 @@
|
|||
libraries += libutilc
|
||||
|
||||
libutilc_NAME = libnixutilc
|
||||
|
||||
libutilc_DIR := $(d)
|
||||
|
||||
libutilc_SOURCES := $(wildcard $(d)/*.cc)
|
||||
|
||||
# Not just for this library itself, but also for downstream libraries using this library
|
||||
|
||||
INCLUDE_libutilc := -I $(d)
|
||||
libutilc_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc)
|
||||
|
||||
libutilc_LIBS = libutil
|
||||
|
||||
libutilc_LDFLAGS += -pthread
|
||||
|
||||
libutilc_FORCE_INSTALL := 1
|
148
src/libutil-c/nix_api_util.cc
Normal file
148
src/libutil-c/nix_api_util.cc
Normal file
|
@ -0,0 +1,148 @@
|
|||
#include "nix_api_util.h"
|
||||
#include "config.hh"
|
||||
#include "error.hh"
|
||||
#include "nix_api_util_internal.h"
|
||||
#include "util.hh"
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <typeinfo>
|
||||
|
||||
nix_c_context * nix_c_context_create()
|
||||
{
|
||||
return new nix_c_context();
|
||||
}
|
||||
|
||||
void nix_c_context_free(nix_c_context * context)
|
||||
{
|
||||
delete context;
|
||||
}
|
||||
|
||||
nix_err nix_context_error(nix_c_context * context)
|
||||
{
|
||||
if (context == nullptr) {
|
||||
throw;
|
||||
}
|
||||
try {
|
||||
throw;
|
||||
} catch (nix::Error & e) {
|
||||
/* Storing this exception is annoying, take what we need here */
|
||||
context->last_err = e.what();
|
||||
context->info = e.info();
|
||||
int status;
|
||||
const char * demangled = abi::__cxa_demangle(typeid(e).name(), 0, 0, &status);
|
||||
if (demangled) {
|
||||
context->name = demangled;
|
||||
// todo: free(demangled);
|
||||
} else {
|
||||
context->name = typeid(e).name();
|
||||
}
|
||||
context->last_err_code = NIX_ERR_NIX_ERROR;
|
||||
return context->last_err_code;
|
||||
} catch (const std::exception & e) {
|
||||
context->last_err = e.what();
|
||||
context->last_err_code = NIX_ERR_UNKNOWN;
|
||||
return context->last_err_code;
|
||||
}
|
||||
// unreachable
|
||||
}
|
||||
|
||||
nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg)
|
||||
{
|
||||
if (context == nullptr) {
|
||||
// todo last_err_code
|
||||
throw nix::Error("Nix C api error: %s", msg);
|
||||
}
|
||||
context->last_err_code = err;
|
||||
context->last_err = msg;
|
||||
return err;
|
||||
}
|
||||
|
||||
const char * nix_version_get()
|
||||
{
|
||||
return PACKAGE_VERSION;
|
||||
}
|
||||
|
||||
// Implementations
|
||||
|
||||
nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
std::map<std::string, nix::AbstractConfig::SettingInfo> settings;
|
||||
nix::globalConfig.getSettings(settings);
|
||||
if (settings.contains(key)) {
|
||||
return call_nix_observe_string(settings[key].value, callback, user_data);
|
||||
} else {
|
||||
return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found");
|
||||
}
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_setting_set(nix_c_context * context, const char * key, const char * value)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
if (nix::globalConfig.set(key, value))
|
||||
return NIX_OK;
|
||||
else {
|
||||
return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found");
|
||||
}
|
||||
}
|
||||
|
||||
nix_err nix_libutil_init(nix_c_context * context)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
nix::initLibUtil();
|
||||
return NIX_OK;
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_context, unsigned int * n)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
if (read_context->last_err) {
|
||||
if (n)
|
||||
*n = read_context->last_err->size();
|
||||
return read_context->last_err->c_str();
|
||||
}
|
||||
nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
if (read_context->last_err_code != NIX_ERR_NIX_ERROR) {
|
||||
return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error");
|
||||
}
|
||||
return call_nix_observe_string(read_context->name, callback, user_data);
|
||||
}
|
||||
|
||||
nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
if (read_context->last_err_code != NIX_ERR_NIX_ERROR) {
|
||||
return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error");
|
||||
}
|
||||
return call_nix_observe_string(read_context->info->msg.str(), callback, user_data);
|
||||
}
|
||||
|
||||
nix_err nix_err_code(const nix_c_context * read_context)
|
||||
{
|
||||
return read_context->last_err_code;
|
||||
}
|
||||
|
||||
// internal
|
||||
nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data)
|
||||
{
|
||||
((nix_observe_string) callback)(str.c_str(), str.size(), user_data);
|
||||
return NIX_OK;
|
||||
}
|
301
src/libutil-c/nix_api_util.h
Normal file
301
src/libutil-c/nix_api_util.h
Normal file
|
@ -0,0 +1,301 @@
|
|||
#ifndef NIX_API_UTIL_H
|
||||
#define NIX_API_UTIL_H
|
||||
/**
|
||||
* @defgroup libutil libutil
|
||||
* @brief C bindings for nix libutil
|
||||
*
|
||||
* libutil is used for functionality shared between
|
||||
* different Nix modules.
|
||||
* @{
|
||||
*/
|
||||
/** @file
|
||||
* @brief Main entry for the libutil C bindings
|
||||
*
|
||||
* Also contains error handling utilities
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// cffi start
|
||||
|
||||
/** @defgroup errors Handling errors
|
||||
* @brief Dealing with errors from the Nix side
|
||||
*
|
||||
* To handle errors that can be returned from the Nix API,
|
||||
* a nix_c_context can be passed to any function that potentially returns an
|
||||
* error.
|
||||
*
|
||||
* Error information will be stored in this context, and can be retrieved
|
||||
* using nix_err_code and nix_err_msg.
|
||||
*
|
||||
* Passing NULL instead will cause the API to throw C++ errors.
|
||||
*
|
||||
* Example:
|
||||
* @code{.c}
|
||||
* int main() {
|
||||
* nix_c_context* ctx = nix_c_context_create();
|
||||
* nix_libutil_init(ctx);
|
||||
* if (nix_err_code(ctx) != NIX_OK) {
|
||||
* printf("error: %s\n", nix_err_msg(NULL, ctx, NULL));
|
||||
* return 1;
|
||||
* }
|
||||
* return 0;
|
||||
* }
|
||||
* @endcode
|
||||
* @{
|
||||
*/
|
||||
// Error codes
|
||||
/**
|
||||
* @brief Type for error codes in the NIX system
|
||||
*
|
||||
* This type can have one of several predefined constants:
|
||||
* - NIX_OK: No error occurred (0)
|
||||
* - NIX_ERR_UNKNOWN: An unknown error occurred (-1)
|
||||
* - NIX_ERR_OVERFLOW: An overflow error occurred (-2)
|
||||
* - NIX_ERR_KEY: A key error occurred (-3)
|
||||
* - NIX_ERR_NIX_ERROR: A generic Nix error occurred (-4)
|
||||
*/
|
||||
typedef int nix_err;
|
||||
|
||||
/**
|
||||
* @brief No error occurred.
|
||||
*
|
||||
* This error code is returned when no error has occurred during the function
|
||||
* execution.
|
||||
*/
|
||||
#define NIX_OK 0
|
||||
|
||||
/**
|
||||
* @brief An unknown error occurred.
|
||||
*
|
||||
* This error code is returned when an unknown error occurred during the
|
||||
* function execution.
|
||||
*/
|
||||
#define NIX_ERR_UNKNOWN -1
|
||||
|
||||
/**
|
||||
* @brief An overflow error occurred.
|
||||
*
|
||||
* This error code is returned when an overflow error occurred during the
|
||||
* function execution.
|
||||
*/
|
||||
#define NIX_ERR_OVERFLOW -2
|
||||
|
||||
/**
|
||||
* @brief A key error occurred.
|
||||
*
|
||||
* This error code is returned when a key error occurred during the function
|
||||
* execution.
|
||||
*/
|
||||
#define NIX_ERR_KEY -3
|
||||
|
||||
/**
|
||||
* @brief A generic Nix error occurred.
|
||||
*
|
||||
* This error code is returned when a generic Nix error occurred during the
|
||||
* function execution.
|
||||
*/
|
||||
#define NIX_ERR_NIX_ERROR -4
|
||||
|
||||
/**
|
||||
* @brief This object stores error state.
|
||||
* @struct nix_c_context
|
||||
*
|
||||
* Passed as a first parameter to functions that can fail, to store error
|
||||
* information.
|
||||
*
|
||||
* Optional wherever it can be used, passing NULL instead will throw a C++
|
||||
* exception.
|
||||
*
|
||||
* The struct is laid out so that it can also be cast to nix_err* to inspect
|
||||
* directly:
|
||||
* @code{.c}
|
||||
* assert(*(nix_err*)ctx == NIX_OK);
|
||||
* @endcode
|
||||
* @note These can be reused between different function calls,
|
||||
* but make sure not to use them for multiple calls simultaneously (which can
|
||||
* happen in callbacks).
|
||||
*/
|
||||
typedef struct nix_c_context nix_c_context;
|
||||
|
||||
/**
|
||||
* @brief Called to get the value of a string owned by Nix.
|
||||
*
|
||||
* @param[in] start the string to copy.
|
||||
* @param[in] n the string length.
|
||||
* @param[in] user_data optional, arbitrary data, passed to the nix_observe_string when it's called.
|
||||
*/
|
||||
typedef void (*nix_observe_string)(const char * start, unsigned int n, void * user_data);
|
||||
|
||||
// Function prototypes
|
||||
|
||||
/**
|
||||
* @brief Allocate a new nix_c_context.
|
||||
* @throws std::bad_alloc
|
||||
* @return allocated nix_c_context, owned by the caller. Free using
|
||||
* `nix_c_context_free`.
|
||||
*/
|
||||
nix_c_context * nix_c_context_create();
|
||||
/**
|
||||
* @brief Free a nix_c_context. Does not fail.
|
||||
* @param[out] context The context to free, mandatory.
|
||||
*/
|
||||
void nix_c_context_free(nix_c_context * context);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initializes nix_libutil and its dependencies.
|
||||
*
|
||||
* This function can be called multiple times, but should be called at least
|
||||
* once prior to any other nix function.
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @return NIX_OK if the initialization is successful, or an error code
|
||||
* otherwise.
|
||||
*/
|
||||
nix_err nix_libutil_init(nix_c_context * context);
|
||||
|
||||
/** @defgroup settings
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Retrieves a setting from the nix global configuration.
|
||||
*
|
||||
* This function requires nix_libutil_init() to be called at least once prior to
|
||||
* its use.
|
||||
*
|
||||
* @param[out] context optional, Stores error information
|
||||
* @param[in] key The key of the setting to retrieve.
|
||||
* @param[in] callback Called with the setting value.
|
||||
* @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
|
||||
* @see nix_observe_string
|
||||
* @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was retrieved
|
||||
* successfully.
|
||||
*/
|
||||
nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data);
|
||||
|
||||
/**
|
||||
* @brief Sets a setting in the nix global configuration.
|
||||
*
|
||||
* Use "extra-<setting name>" to append to the setting's value.
|
||||
*
|
||||
* Settings only apply for new State%s. Call nix_plugins_init() when you are
|
||||
* done with the settings to load any plugins.
|
||||
*
|
||||
* @param[out] context optional, Stores error information
|
||||
* @param[in] key The key of the setting to set.
|
||||
* @param[in] value The value to set for the setting.
|
||||
* @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was
|
||||
* set successfully.
|
||||
*/
|
||||
nix_err nix_setting_set(nix_c_context * context, const char * key, const char * value);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
// todo: nix_plugins_init()
|
||||
|
||||
/**
|
||||
* @brief Retrieves the nix library version.
|
||||
*
|
||||
* Does not fail.
|
||||
* @return A static string representing the version of the nix library.
|
||||
*/
|
||||
const char * nix_version_get();
|
||||
|
||||
/** @addtogroup errors
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Retrieves the most recent error message from a context.
|
||||
*
|
||||
* @pre This function should only be called after a previous nix function has
|
||||
* returned an error.
|
||||
*
|
||||
* @param[out] context optional, the context to store errors in if this function
|
||||
* fails
|
||||
* @param[in] ctx the context to retrieve the error message from
|
||||
* @param[out] n optional: a pointer to an unsigned int that is set to the
|
||||
* length of the error.
|
||||
* @return nullptr if no error message was ever set,
|
||||
* a borrowed pointer to the error message otherwise.
|
||||
*/
|
||||
const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, unsigned int * n);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the error message from errorInfo in a context.
|
||||
*
|
||||
* Used to inspect nix Error messages.
|
||||
*
|
||||
* @pre This function should only be called after a previous nix function has
|
||||
* returned a NIX_ERR_NIX_ERROR
|
||||
*
|
||||
* @param[out] context optional, the context to store errors in if this function
|
||||
* fails
|
||||
* @param[in] read_context the context to retrieve the error message from.
|
||||
* @param[in] callback Called with the error message.
|
||||
* @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
|
||||
* @see nix_observe_string
|
||||
* @return NIX_OK if there were no errors, an error code otherwise.
|
||||
*/
|
||||
nix_err
|
||||
nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the error name from a context.
|
||||
*
|
||||
* Used to inspect nix Error messages.
|
||||
*
|
||||
* @pre This function should only be called after a previous nix function has
|
||||
* returned a NIX_ERR_NIX_ERROR
|
||||
*
|
||||
* @param context optional, the context to store errors in if this function
|
||||
* fails
|
||||
* @param[in] read_context the context to retrieve the error message from
|
||||
* @param[in] callback Called with the error name.
|
||||
* @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
|
||||
* @see nix_observe_string
|
||||
* @return NIX_OK if there were no errors, an error code otherwise.
|
||||
*/
|
||||
nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the most recent error code from a nix_c_context
|
||||
*
|
||||
* Equivalent to reading the first field of the context.
|
||||
*
|
||||
* Does not fail
|
||||
*
|
||||
* @param[in] read_context the context to retrieve the error message from
|
||||
* @return most recent error code stored in the context.
|
||||
*/
|
||||
nix_err nix_err_code(const nix_c_context * read_context);
|
||||
|
||||
/**
|
||||
* @brief Set an error message on a nix context.
|
||||
*
|
||||
* This should be used when you want to throw an error from a PrimOp callback.
|
||||
*
|
||||
* All other use is internal to the API.
|
||||
*
|
||||
* @param context context to write the error message to, or NULL
|
||||
* @param err The error code to set and return
|
||||
* @param msg The error message to set.
|
||||
* @returns the error code set
|
||||
*/
|
||||
nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
// cffi end
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
#endif // NIX_API_UTIL_H
|
49
src/libutil-c/nix_api_util_internal.h
Normal file
49
src/libutil-c/nix_api_util_internal.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef NIX_API_UTIL_INTERNAL_H
|
||||
#define NIX_API_UTIL_INTERNAL_H
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include "error.hh"
|
||||
#include "nix_api_util.h"
|
||||
|
||||
struct nix_c_context
|
||||
{
|
||||
nix_err last_err_code = NIX_OK;
|
||||
std::optional<std::string> last_err = {};
|
||||
std::optional<nix::ErrorInfo> info = {};
|
||||
std::string name = "";
|
||||
};
|
||||
|
||||
nix_err nix_context_error(nix_c_context * context);
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* Helper to invoke nix_observe_string
|
||||
* @param context optional, the context to store errors in if this function
|
||||
* fails
|
||||
* @param str The string to observe
|
||||
* @param callback Called with the observed string.
|
||||
* @param user_data optional, arbitrary data, passed to the callback when it's called.
|
||||
* @return NIX_OK if there were no errors.
|
||||
* @see nix_observe_string
|
||||
*/
|
||||
nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data);
|
||||
|
||||
#define NIXC_CATCH_ERRS \
|
||||
catch (...) \
|
||||
{ \
|
||||
return nix_context_error(context); \
|
||||
} \
|
||||
return NIX_OK;
|
||||
|
||||
#define NIXC_CATCH_ERRS_RES(def) \
|
||||
catch (...) \
|
||||
{ \
|
||||
nix_context_error(context); \
|
||||
return def; \
|
||||
}
|
||||
#define NIXC_CATCH_ERRS_NULL NIXC_CATCH_ERRS_RES(nullptr)
|
||||
|
||||
#endif // NIX_API_UTIL_INTERNAL_H
|
Loading…
Add table
Add a link
Reference in a new issue