A lightweight environment variables manager designed specifically for the DMOD (Dynamic Modules) framework. dmenv provides a simple and efficient way to manage environment variables in embedded systems with limited resources.
- Overview
- What is DMOD?
- What is dmenv?
- Features
- Building
- Testing
- Usage
- API Reference
- Contributing
- License
dmenv is a custom environment variables manager that integrates seamlessly with the DMOD dynamic module system. It provides a context-based solution for storing and retrieving configuration parameters, making it ideal for embedded systems where flexibility and modularity are important. With support for context inheritance, multiple isolated environments can be managed efficiently.
DMOD (Dynamic Modules) is a library that enables dynamic loading and unloading of modules in embedded systems at runtime. It allows you to:
- Dynamically load modules: Load functionality from
.dmffiles without recompiling - Manage dependencies: Automatically handle module dependencies
- Inter-module communication: Modules can communicate via a common API
- Resource management: Efficiently manage system resources
- Safe updates: Update individual modules without affecting the entire system
DMOD provides a modular architecture that makes embedded systems more flexible, maintainable, and easier to extend. For more information, visit the DMOD repository.
dmenv is an environment variables manager specifically designed to work with DMOD. It provides:
- Simple key-value storage: Store configuration parameters as name-value pairs
- Context-based management: Create multiple isolated environment contexts
- Context inheritance: Child contexts can inherit variables from parent contexts
- Context stack: Push and pop contexts for temporary context switching
- Thread-safe operations: Uses DMOD's critical section mechanisms
- Prefix-based search: Find all variables matching a specific prefix
- Efficient lookup: Fast variable retrieval using linked list structure
- Integer support: Set and get variables as unsigned integers (hex or decimal)
- Module integration: Seamless integration with DMOD logging and error handling
dmenv is ideal for storing application configuration, runtime parameters, and inter-module communication data in embedded DMOD-based systems.
- âś… Context-based management: Create multiple isolated environment contexts
- âś… Context inheritance: Child contexts can inherit variables from parent contexts
- âś… Context stack: Push and pop contexts for temporary context switching
- âś… Root context: Set a global root context as the base fallback
- âś… Key-value storage: Store and retrieve environment variables by name
- âś… Variable update: Update existing variables without creating duplicates
- âś… Variable removal: Remove individual variables or clear all at once
- âś… Prefix search: Find all variables matching a given prefix
- âś… Variable counting: Track the number of stored variables
- âś… Integer support: Set and get variables as unsigned integers (hex or decimal)
- âś… Dynamic allocation: Uses DMOD's memory management system
- âś… Thread-safe: Uses DMOD critical sections for synchronization
- âś… Comprehensive logging: Integration with DMOD logging system
- âś… Zero external dependencies: Only requires DMOD framework
- CMake: Version 3.10 or higher
- C Compiler: GCC or compatible
- Make: For Makefile-based builds (optional)
# Clone the repository
git clone https://github.com/choco-technologies/dmenv.git
cd dmenv
# Create build directory
mkdir build
cd build
# Configure
cmake ..
# Build
make
# Run tests
ctest --verboseYou can customize the build with these CMake options:
# Enable tests
cmake -DDMENV_BUILD_TESTS=ON ..
# Disable DMOD API implementation
cmake -DDMENV_DONT_IMPLEMENT_DMOD_API=ON ..
# Change DMOD mode
cmake -DDMOD_MODE=DMOD_EMBEDDED ..dmenv also supports traditional Makefile builds:
# Build the library
make
# The library will be created as libdmenv.admenv includes comprehensive test suites:
-
test_minimal: Minimal smoke tests
- Context creation and destruction
- Basic set/get operations
- Count and remove operations
- Integer operations
-
test_simple: Simple functional tests
- Set and get variables
- Remove variables
- Clear all variables
- Find variables by prefix
- Integer operations
- Context inheritance
-
test_dmenv_unit: Comprehensive unit tests using Unity framework
- Context creation and validation
- Set/get operations
- Update existing variables
- Remove operations
- Clear all variables
- Count operations
- Find operations with prefixes
- Integer operations (seti/geti)
- Context inheritance
- Context override behavior
- Root context management
- Context stack push/pop operations
- Multiple variable stress tests
# Run all tests
cd build
ctest
# Run with verbose output
ctest --verbose
# Run specific test
./tests/test_minimal
./tests/test_simple
./tests/test_dmenv_unit#include "dmenv.h"
#include <string.h>
int main(void) {
// 1. Create an environment context
dmenv_ctx_t ctx = dmenv_create(NULL);
if (ctx == NULL) {
// Handle creation failure
return -1;
}
// 2. Set environment variables
dmenv_set(ctx, "APP_NAME", "MyApplication");
dmenv_set(ctx, "APP_VERSION", "1.0.0");
dmenv_set(ctx, "DEBUG_MODE", "true");
// 3. Get environment variables
const char* app_name = dmenv_get(ctx, "APP_NAME");
if (app_name != NULL) {
printf("Application: %s\n", app_name);
}
// 4. Clean up
dmenv_destroy(ctx);
return 0;
}#include "dmenv.h"
void config_example(void) {
dmenv_ctx_t ctx = dmenv_create(NULL);
// Set initial value
dmenv_set(ctx, "LOG_LEVEL", "INFO");
// Update the value (no duplicate created)
dmenv_set(ctx, "LOG_LEVEL", "DEBUG");
// Get updated value
const char* level = dmenv_get(ctx, "LOG_LEVEL");
// level now contains "DEBUG"
dmenv_destroy(ctx);
}#include "dmenv.h"
void cleanup_example(void) {
dmenv_ctx_t ctx = dmenv_create(NULL);
// Set a temporary variable
dmenv_set(ctx, "TEMP_DATA", "temporary_value");
// Remove it when done
bool removed = dmenv_remove(ctx, "TEMP_DATA");
if (removed) {
printf("Temporary data removed\n");
}
// Variable no longer exists
const char* value = dmenv_get(ctx, "TEMP_DATA");
// value is NULL
dmenv_destroy(ctx);
}#include "dmenv.h"
void print_var(const char* name, const char* value, void* user_data) {
printf("%s = %s\n", name, value);
}
void find_example(void) {
dmenv_ctx_t ctx = dmenv_create(NULL);
// Set related variables with common prefix
dmenv_set(ctx, "DB_HOST", "localhost");
dmenv_set(ctx, "DB_PORT", "5432");
dmenv_set(ctx, "DB_NAME", "mydb");
dmenv_set(ctx, "APP_NAME", "myapp");
// Find all database-related variables
printf("Database configuration:\n");
size_t found = dmenv_find(ctx, "DB_", print_var, NULL);
printf("Found %zu database variables\n", found);
dmenv_destroy(ctx);
}#include "dmenv.h"
void reset_example(void) {
dmenv_ctx_t ctx = dmenv_create(NULL);
// Clear all environment variables
dmenv_clear(ctx);
// Verify count is 0
size_t count = dmenv_count(ctx);
printf("Variables remaining: %zu\n", count); // Prints: 0
dmenv_destroy(ctx);
}#include "dmenv.h"
void context_stack_example(void) {
// Create root and temporary contexts
dmenv_ctx_t root = dmenv_create(NULL);
dmenv_ctx_t temp = dmenv_create(NULL);
// Set root context as the base
dmenv_set_root_context(root);
dmenv_set(root, "MODE", "production");
// Get current context (returns root)
dmenv_ctx_t current = dmenv_get_current_context();
printf("Mode: %s\n", dmenv_get(current, "MODE")); // Prints: production
// Push temporary context for testing
dmenv_push_context(temp);
dmenv_set(temp, "MODE", "testing");
// Get current context (returns temp)
current = dmenv_get_current_context();
printf("Mode: %s\n", dmenv_get(current, "MODE")); // Prints: testing
// Pop temporary context
dmenv_pop_context();
// Get current context (returns root again)
current = dmenv_get_current_context();
printf("Mode: %s\n", dmenv_get(current, "MODE")); // Prints: production
dmenv_destroy(temp);
dmenv_destroy(root);
}#include "dmenv.h"
#include "dmod.h"
#include <stdio.h>
void load_configuration(dmenv_ctx_t ctx) {
// Load configuration from persistent storage or hardcode
dmenv_set(ctx, "SYSTEM_NAME", "EmbeddedDevice");
dmenv_set(ctx, "FIRMWARE_VERSION", "2.1.3");
dmenv_set(ctx, "NETWORK_ENABLED", "true");
dmenv_set(ctx, "NETWORK_IP", "192.168.1.100");
dmenv_set(ctx, "NETWORK_PORT", "8080");
dmenv_set(ctx, "SENSOR_SAMPLE_RATE", "1000");
dmenv_set(ctx, "SENSOR_THRESHOLD", "75.5");
}
void print_all_network_settings(const char* name, const char* value, void* user_data) {
printf(" %s = %s\n", name, value);
}
int main(void) {
printf("=== Embedded System Configuration ===\n\n");
// Create environment context
dmenv_ctx_t ctx = dmenv_create(NULL);
if (ctx == NULL) {
printf("ERROR: Failed to create environment context\n");
return -1;
}
// Load configuration
load_configuration(ctx);
printf("Configuration loaded: %zu variables\n\n", dmenv_count(ctx));
// Display system info
printf("System: %s\n", dmenv_get(ctx, "SYSTEM_NAME"));
printf("Firmware: %s\n\n", dmenv_get(ctx, "FIRMWARE_VERSION"));
// Display network settings
printf("Network Configuration:\n");
dmenv_find(ctx, "NETWORK_", print_all_network_settings, NULL);
// Display sensor settings
printf("\nSensor Configuration:\n");
dmenv_find(ctx, "SENSOR_", print_all_network_settings, NULL);
// Clean up
dmenv_destroy(ctx);
return 0;
}dmenv_ctx_t dmenv_create(dmenv_ctx_t parent);Create a new environment variables context.
- Parameters:
parent: Optional parent context for variable inheritance. If a variable is not found in the current context, it will be searched in the parent context. Pass NULL for no inheritance.
- Returns: Pointer to the created context, or NULL on failure
- Thread-safe: Yes
void dmenv_destroy(dmenv_ctx_t ctx);Destroy an environment variables context.
- Parameters:
ctx: Context to destroy
- Thread-safe: Yes
bool dmenv_is_valid(dmenv_ctx_t ctx);Check if a context is valid.
- Parameters:
ctx: Context to check
- Returns:
trueif valid,falseotherwise - Thread-safe: Yes
void dmenv_set_root_context(dmenv_ctx_t ctx);Set the root context. The root context serves as the base context when no other contexts have been pushed onto the context stack.
- Parameters:
ctx: Context to set as root context
- Thread-safe: Yes
dmenv_ctx_t dmenv_get_root_context(void);Get the root context.
- Returns: Pointer to the root context, or NULL if not set
- Thread-safe: Yes
bool dmenv_push_context(dmenv_ctx_t ctx);Push a context onto the context stack. The pushed context becomes the current context.
- Parameters:
ctx: Context to push onto the stack
- Returns:
trueif the context was pushed successfully,falseotherwise (e.g., stack overflow or invalid context) - Thread-safe: Yes
dmenv_ctx_t dmenv_pop_context(void);Pop the current context from the context stack. After popping, the previous context on the stack becomes the current context. If the stack is empty, the root context becomes the current context.
- Returns: Pointer to the popped context, or NULL if the stack was empty
- Thread-safe: Yes
dmenv_ctx_t dmenv_get_current_context(void);Get the current context. Returns the top context from the stack if any contexts have been pushed, otherwise returns the root context.
- Returns: Pointer to the current context, or NULL if no context is set
- Thread-safe: Yes
bool dmenv_set(dmenv_ctx_t ctx, const char* name, const char* value);Set an environment variable. If the variable already exists, its value is updated.
- Parameters:
ctx: Context to set the variable inname: Name of the environment variablevalue: Value to set
- Returns:
trueif the variable was set successfully,falseotherwise - Thread-safe: Yes
const char* dmenv_get(dmenv_ctx_t ctx, const char* name);Get an environment variable value. If the variable is not found in the context and the context has a parent, the parent will be searched recursively.
- Parameters:
ctx: Context to get the variable fromname: Name of the environment variable
- Returns: Pointer to the value string, or NULL if not found
- Thread-safe: Yes
- Note: The returned pointer is valid until the variable is removed or modified
bool dmenv_seti(dmenv_ctx_t ctx, const char* name, uint32_t value);Set an environment variable (unsigned integer value in hex). The value is stored internally as a hexadecimal string (e.g., "0x2000").
- Parameters:
ctx: Context to set the variable inname: Name of the environment variablevalue: Unsigned integer value to set
- Returns:
trueif the variable was set successfully,falseotherwise - Thread-safe: Yes
bool dmenv_geti(dmenv_ctx_t ctx, const char* name, uint32_t* out_value);Get an environment variable value (unsigned integer). Parses the value as a hexadecimal or decimal number. If the variable is not found in the context and the context has a parent, the parent will be searched.
- Parameters:
ctx: Context to get the variable fromname: Name of the environment variableout_value: Pointer to store the parsed value
- Returns:
trueif the variable was found and parsed successfully,falseotherwise - Thread-safe: Yes
bool dmenv_remove(dmenv_ctx_t ctx, const char* name);Remove an environment variable. Only removes from the current context, not from parent contexts.
- Parameters:
ctx: Context to remove the variable fromname: Name of the environment variable to remove
- Returns:
trueif the variable was removed successfully,falseif not found - Thread-safe: Yes
bool dmenv_clear(dmenv_ctx_t ctx);Clear all environment variables in a context. Only clears variables in the current context, not in parent contexts.
- Parameters:
ctx: Context to clear
- Returns:
trueif all variables were cleared successfully,falseotherwise - Thread-safe: Yes
size_t dmenv_find(dmenv_ctx_t ctx, const char* prefix,
void (*callback)(const char* name, const char* value, void* user_data),
void* user_data);Find environment variables matching a prefix. Only searches in the current context, not in parent contexts.
- Parameters:
ctx: Context to search inprefix: Prefix to match against variable namescallback: Callback function to call for each matching variableuser_data: User data to pass to the callback
- Returns: Number of matching variables found
- Thread-safe: Yes
size_t dmenv_count(dmenv_ctx_t ctx);Get the number of environment variables in a context. Only counts variables in the current context, not in parent contexts.
- Parameters:
ctx: Context to count variables in
- Returns: Number of environment variables
- Thread-safe: Yes
Contributions are welcome! Please feel free to submit issues, fork the repository, and create pull requests.
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/dmenv.git - Create a feature branch:
git checkout -b feature/my-new-feature - Make your changes and add tests
- Run tests:
cd build && ctest - Commit your changes:
git commit -am 'Add some feature' - Push to the branch:
git push origin feature/my-new-feature - Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- DMOD - Dynamic Module Loading Framework
- dmheap - DMOD Heap Memory Manager
- dmlog - DMOD Logging System
For more information and support, please visit the dmenv repository or contact the Choco-Technologies team.