error handling layer implemented, header boilerplated for licensing info
parent
e30ed309bd
commit
a5e2c86882
|
@ -11,6 +11,10 @@ add_executable(
|
|||
src/dpm.cpp
|
||||
src/ModuleLoader.cpp
|
||||
src/dpm_interface.cpp
|
||||
src/error.cpp
|
||||
include/dpm_interface_helpers.hpp
|
||||
src/dpm_interface_helpers.cpp
|
||||
src/handlers.cpp
|
||||
)
|
||||
|
||||
target_include_directories(dpm PRIVATE include)
|
||||
|
@ -19,7 +23,7 @@ target_link_libraries(dpm dl)
|
|||
# Add the info module
|
||||
add_library(info MODULE modules/info.cpp)
|
||||
set_target_properties(info PROPERTIES
|
||||
PREFIX "" # Remove lib prefix
|
||||
PREFIX ""
|
||||
SUFFIX ".so"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
|
||||
)
|
||||
|
|
|
@ -1,32 +1,71 @@
|
|||
/**
|
||||
* @file ModuleLoader.hpp
|
||||
* @brief Dynamic module loading and management for DPM
|
||||
*
|
||||
* Defines the ModuleLoader class which is responsible for finding, loading,
|
||||
* validating, and executing DPM modules. It handles the dynamic loading of
|
||||
* shared objects and ensures they conform to the expected module interface.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 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/>.
|
||||
*
|
||||
* For bug reports or contributions, please contact the dhlp-contributors
|
||||
* mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "error.hpp"
|
||||
#include <filesystem>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "module_interface.hpp"
|
||||
|
||||
|
||||
class ModuleLoader {
|
||||
public:
|
||||
// initializer
|
||||
explicit ModuleLoader(std::string module_path = "/usr/lib/dpm/modules/");
|
||||
DPMError list_available_modules(std::vector<std::string>& modules) const;
|
||||
DPMError get_module_path(std::string& path) const;
|
||||
public:
|
||||
// initializer
|
||||
explicit ModuleLoader(std::string module_path = "/usr/lib/dpm/modules/");
|
||||
DPMErrorCategory list_available_modules(std::vector<std::string>& modules) const;
|
||||
DPMErrorCategory get_module_path(std::string& path) const;
|
||||
|
||||
// Load and execute methods
|
||||
DPMError load_module(const std::string& module_name, void*& module_handle) const;
|
||||
DPMError execute_module(const std::string& module_name, const std::string& command) const;
|
||||
// Load and execute methods
|
||||
DPMErrorCategory load_module(const std::string& module_name, void*& module_handle) const;
|
||||
DPMErrorCategory execute_module(const std::string& module_name, const std::string& command) const;
|
||||
|
||||
// Get module version
|
||||
DPMError get_module_version(void* module_handle, std::string& version) const;
|
||||
// Get module version
|
||||
DPMErrorCategory get_module_version(void* module_handle, std::string& version) const;
|
||||
|
||||
// Get module description
|
||||
DPMError get_module_description(void* module_handle, std::string& description) const;
|
||||
// Get module description
|
||||
DPMErrorCategory get_module_description(void* module_handle, std::string& description) const;
|
||||
|
||||
// Check if all required symbols from module_interface.hpp are exported by the module
|
||||
DPMError validate_module_interface(void* module_handle, std::vector<std::string>& missing_symbols) const;
|
||||
// Check if all required symbols from module_interface.hpp are exported by the module
|
||||
DPMErrorCategory validate_module_interface(void* module_handle, std::vector<std::string>& missing_symbols) const;
|
||||
|
||||
private:
|
||||
std::string _module_path;
|
||||
// Helper method to check module path validity
|
||||
DPMErrorCategory check_module_path() const;
|
||||
|
||||
private:
|
||||
std::string _module_path;
|
||||
};
|
|
@ -1,12 +1,44 @@
|
|||
/**
|
||||
* @file dpm_interface.hpp
|
||||
* @brief Interface declarations for the DPM command-line functionality
|
||||
*
|
||||
* Defines the public interface methods that provide human-readable interaction
|
||||
* with the DPM core functionality, including module path validation and
|
||||
* module listing capabilities.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 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/>.
|
||||
*
|
||||
* For bug reports or contributions, please contact the dhlp-contributors
|
||||
* mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <getopt.h>
|
||||
#include <vector>
|
||||
#include "error.hpp"
|
||||
#include <dlfcn.h>
|
||||
#include <iomanip>
|
||||
#include <filesystem>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "ModuleLoader.hpp"
|
||||
#include "dpm_interface_helpers.hpp"
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -21,16 +53,3 @@ int main_check_module_path(const ModuleLoader& loader);
|
|||
|
||||
// list the modules
|
||||
int main_list_modules(const ModuleLoader& loader);
|
||||
|
||||
// data structure for supplied arguments
|
||||
struct CommandArgs {
|
||||
std::string module_path;
|
||||
std::string module_name;
|
||||
std::string command; // All arguments combined into a single command string
|
||||
};
|
||||
|
||||
// parser for populating data structure for supplied arguments
|
||||
CommandArgs parse_args(int argc, char* argv[]);
|
||||
|
||||
// pairs DPMErrors to error messages, prints those error messages, and returns
|
||||
int print_error(DPMError error, const std::string& module_name, const std::string& module_path);
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* @file dpm_interface_helpers.hpp
|
||||
* @brief Helper functions for DPM command-line interface
|
||||
*
|
||||
* Provides utility functions for command-line argument parsing and
|
||||
* data structures for representing command arguments in a structured format.
|
||||
* These helpers are used by the main DPM interface to process user input.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 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/>.
|
||||
*
|
||||
* For bug reports or contributions, please contact the dhlp-contributors
|
||||
* mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <getopt.h>
|
||||
|
||||
// data structure for supplied arguments
|
||||
struct CommandArgs {
|
||||
std::string module_path;
|
||||
std::string module_name;
|
||||
std::string command; // All arguments combined into a single command string
|
||||
};
|
||||
|
||||
// parse dpm cli arguments into a serialized structure
|
||||
CommandArgs parse_args( int argc, char * argv[] );
|
|
@ -1,10 +1,42 @@
|
|||
/**
|
||||
* @file error.hpp
|
||||
* @brief Error handling system for the DPM utility
|
||||
*
|
||||
* Defines the error categories, error context structure, and utility
|
||||
* functions for error handling throughout the DPM system. Provides a
|
||||
* consistent approach to error reporting and management.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 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/>.
|
||||
*
|
||||
* For bug reports or contributions, please contact the dhlp-contributors
|
||||
* mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
|
||||
// global errors for the core DPM routing/execution component
|
||||
enum class DPMError {
|
||||
enum class DPMErrorCategory {
|
||||
SUCCESS,
|
||||
PATH_NOT_FOUND,
|
||||
PATH_NOT_DIRECTORY,
|
||||
PATH_TOO_LONG,
|
||||
PERMISSION_DENIED,
|
||||
MODULE_NOT_FOUND,
|
||||
MODULE_NOT_LOADED,
|
||||
|
@ -14,3 +46,17 @@ enum class DPMError {
|
|||
SYMBOL_EXECUTION_FAILED,
|
||||
UNDEFINED_ERROR
|
||||
};
|
||||
|
||||
// A generic context object that can hold any error-specific data
|
||||
// only DPMErrorCategory is required, all other fields are optional
|
||||
typedef struct {
|
||||
DPMErrorCategory error;
|
||||
const char * module_name;
|
||||
const char * module_path;
|
||||
const char * message;
|
||||
// Add other potential fields as needed as all fields beyond error are optional
|
||||
} FlexDPMError;
|
||||
|
||||
// shorthand for creating a FlexDPMError instance
|
||||
FlexDPMError make_error( DPMErrorCategory error_category );
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @file handlers.hpp
|
||||
* @brief Error handling functions for the DPM system
|
||||
*
|
||||
* Defines specialized handler functions for each error category in the DPM
|
||||
* error system. These handlers translate error codes into user-friendly
|
||||
* messages and provide appropriate exit behavior for different error conditions.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 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/>.
|
||||
*
|
||||
* For bug reports or contributions, please contact the dhlp-contributors
|
||||
* mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
|
||||
#include "error.hpp"
|
||||
|
||||
// fatal error routing method
|
||||
int handle_error(FlexDPMError context);
|
||||
|
||||
// declare a required field
|
||||
void validate_field(FlexDPMError context, const char* field_name, const void* field_value);
|
||||
|
||||
// Individual error handler prototypes
|
||||
int handle_path_not_found(FlexDPMError context);
|
||||
int handle_path_not_directory(FlexDPMError context);
|
||||
int handle_path_too_long(FlexDPMError context);
|
||||
int handle_permission_denied(FlexDPMError context);
|
||||
int handle_module_not_found(FlexDPMError context);
|
||||
int handle_module_not_loaded(FlexDPMError context);
|
||||
int handle_module_load_failed(FlexDPMError context);
|
||||
int handle_invalid_module(FlexDPMError context);
|
||||
int handle_symbol_not_found(FlexDPMError context);
|
||||
int handle_symbol_execution_failed(FlexDPMError context);
|
||||
int handle_undefined_error(FlexDPMError context);
|
|
@ -1,3 +1,33 @@
|
|||
/**
|
||||
* @file module_interface.hpp
|
||||
* @brief Defines the interface for DPM modules
|
||||
*
|
||||
* Establishes the required symbols and common interface that all DPM modules
|
||||
* must implement to be loadable and executable by the core DPM system.
|
||||
* This forms the contract between the main DPM application and its modules.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 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/>.
|
||||
*
|
||||
* For bug reports or contributions, please contact the dhlp-contributors
|
||||
* mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
|
|
@ -1,9 +1,37 @@
|
|||
/**
|
||||
* @file info.cpp
|
||||
* @brief Implementation of the DPM info module
|
||||
*
|
||||
* Provides information about the DPM system through a module interface.
|
||||
* This module supports commands for displaying version information,
|
||||
* system details, and module help documentation.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 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/>.
|
||||
*
|
||||
* For bug reports or contributions, please contact the dhlp-contributors
|
||||
* mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "../include/module_interface.hpp"
|
||||
#include <gpgme.h>
|
||||
|
||||
// Implementation of the info module
|
||||
// This module provides information about the DPM system
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// ModuleLoader constructor
|
||||
ModuleLoader::ModuleLoader(std::string module_path)
|
||||
{
|
||||
try {
|
||||
|
@ -17,67 +18,200 @@ ModuleLoader::ModuleLoader(std::string module_path)
|
|||
}
|
||||
}
|
||||
|
||||
DPMError ModuleLoader::get_module_path(std::string& path) const
|
||||
// getter for module path
|
||||
DPMErrorCategory ModuleLoader::get_module_path(std::string& path) const
|
||||
{
|
||||
path = _module_path;
|
||||
return DPMError::SUCCESS;
|
||||
return DPMErrorCategory::SUCCESS;
|
||||
}
|
||||
|
||||
DPMError ModuleLoader::list_available_modules(std::vector<std::string>& modules) const
|
||||
// Helper method to check module path validity
|
||||
DPMErrorCategory ModuleLoader::check_module_path() const
|
||||
{
|
||||
modules.clear();
|
||||
|
||||
try {
|
||||
for (const auto& entry : fs::directory_iterator(_module_path)) {
|
||||
if (entry.is_regular_file()) {
|
||||
std::string filename = entry.path().filename().string();
|
||||
if (filename.size() > 3 && filename.substr(filename.size() - 3) == ".so") {
|
||||
modules.push_back(filename.substr(0, filename.size() - 3));
|
||||
}
|
||||
}
|
||||
// Verify the path exists and get its properties
|
||||
struct stat path_stat;
|
||||
if (stat(_module_path.c_str(), &path_stat) != 0) {
|
||||
// Check errno to determine the specific error
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
return DPMErrorCategory::PATH_NOT_FOUND;
|
||||
case EACCES:
|
||||
return DPMErrorCategory::PERMISSION_DENIED;
|
||||
case ENAMETOOLONG:
|
||||
return DPMErrorCategory::PATH_TOO_LONG;
|
||||
case ENOTDIR:
|
||||
// This happens when a component of the path prefix isn't a directory
|
||||
return DPMErrorCategory::PATH_NOT_DIRECTORY;
|
||||
default:
|
||||
return DPMErrorCategory::UNDEFINED_ERROR;
|
||||
}
|
||||
} catch (const fs::filesystem_error&) {
|
||||
return DPMError::PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
return DPMError::SUCCESS;
|
||||
// At this point stat() succeeded, now check if the final path component is a directory
|
||||
if (!S_ISDIR(path_stat.st_mode)) {
|
||||
return DPMErrorCategory::PATH_NOT_DIRECTORY;
|
||||
}
|
||||
|
||||
// Check read permissions using the stat results
|
||||
if ((path_stat.st_mode & S_IRUSR) == 0) {
|
||||
return DPMErrorCategory::PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
return DPMErrorCategory::SUCCESS;
|
||||
}
|
||||
|
||||
DPMError ModuleLoader::load_module(const std::string& module_name, void*& module_handle) const
|
||||
DPMErrorCategory ModuleLoader::list_available_modules(std::vector<std::string>& modules) const
|
||||
{
|
||||
// ensure we start with an empty vector
|
||||
modules.clear();
|
||||
|
||||
// Check module path using the helper method
|
||||
DPMErrorCategory path_check = check_module_path();
|
||||
if (path_check != DPMErrorCategory::SUCCESS) {
|
||||
return path_check;
|
||||
}
|
||||
|
||||
// prepare to iterate the directory
|
||||
DIR* dir = opendir(_module_path.c_str());
|
||||
if (!dir) {
|
||||
// Check errno to determine the cause of the failure
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
return DPMErrorCategory::PERMISSION_DENIED;
|
||||
case ENOENT:
|
||||
return DPMErrorCategory::PATH_NOT_FOUND;
|
||||
case ENOTDIR:
|
||||
return DPMErrorCategory::PATH_NOT_DIRECTORY;
|
||||
default:
|
||||
return DPMErrorCategory::UNDEFINED_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// read each entry
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
// skip . and ..
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// build the full path
|
||||
std::string full_path = _module_path + entry->d_name;
|
||||
|
||||
// verify it's a file or a symlink
|
||||
struct stat st;
|
||||
if (stat(full_path.c_str(), &st) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if not a regular file or a symlink
|
||||
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get length of filename for boundary checking
|
||||
size_t name_len = strlen(entry->d_name);
|
||||
|
||||
// skip if filename too short to be .so
|
||||
if (name_len <= 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// verify file ends in .so
|
||||
if (strcmp(entry->d_name + name_len - 3, ".so") != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// calculate the module name length
|
||||
size_t module_name_len = name_len - 3;
|
||||
|
||||
// create space for the module name
|
||||
char * module_name = (char*) malloc(module_name_len + 1);
|
||||
if (!module_name) {
|
||||
closedir(dir);
|
||||
return DPMErrorCategory::UNDEFINED_ERROR;
|
||||
}
|
||||
|
||||
// copy the name without .so
|
||||
strncpy(module_name, entry->d_name, module_name_len);
|
||||
module_name[module_name_len] = '\0';
|
||||
|
||||
// add to our list
|
||||
modules.push_back(std::string(module_name));
|
||||
|
||||
// clean up
|
||||
free(module_name);
|
||||
}
|
||||
|
||||
// clean up
|
||||
closedir(dir);
|
||||
|
||||
return DPMErrorCategory::SUCCESS;
|
||||
}
|
||||
|
||||
DPMErrorCategory ModuleLoader::load_module(const std::string& module_name, void*& module_handle) const
|
||||
{
|
||||
// First check if the module exists in the list of available modules
|
||||
std::vector<std::string> available_modules;
|
||||
DPMErrorCategory list_error = list_available_modules( available_modules );
|
||||
|
||||
// if there was a listing error, return that
|
||||
if ( list_error != DPMErrorCategory::SUCCESS ) {
|
||||
return list_error;
|
||||
}
|
||||
|
||||
// Check if the requested module is in the list of available modules
|
||||
bool module_found = false;
|
||||
for ( size_t i = 0; i < available_modules.size(); i++ ) {
|
||||
if ( available_modules[i] == module_name ) {
|
||||
module_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if the supplied module isn't in the list of available modules, return the error
|
||||
if ( !module_found ) {
|
||||
return DPMErrorCategory::MODULE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// construct the path to load the module from based on supplied identifier
|
||||
// DPM uses whatever the file name is
|
||||
std::string module_so_path = _module_path + module_name + ".so";
|
||||
|
||||
|
||||
// go ahead and open the module
|
||||
module_handle = dlopen(module_so_path.c_str(), RTLD_LAZY);
|
||||
if ( !module_handle ) {
|
||||
return DPMError::MODULE_LOAD_FAILED;
|
||||
if (!module_handle) {
|
||||
return DPMErrorCategory::MODULE_LOAD_FAILED;
|
||||
}
|
||||
|
||||
// if there was a loading error, return that
|
||||
const char * load_error = dlerror();
|
||||
if ( load_error != nullptr ) {
|
||||
return DPMError::MODULE_LOAD_FAILED;
|
||||
return DPMErrorCategory::MODULE_LOAD_FAILED;
|
||||
}
|
||||
|
||||
// validate the module's exposed API
|
||||
// return an error if it's not up to spec
|
||||
std::vector<std::string> missing_symbols;
|
||||
DPMError validate_error = validate_module_interface(module_handle, missing_symbols);
|
||||
if ( validate_error != DPMError::SUCCESS ) {
|
||||
dlclose(module_handle);
|
||||
DPMErrorCategory validate_error = validate_module_interface( module_handle, missing_symbols );
|
||||
if ( validate_error != DPMErrorCategory::SUCCESS ) {
|
||||
// we failed to validate the interface, so close the module handle since we won't use it
|
||||
dlclose( module_handle );
|
||||
return validate_error;
|
||||
}
|
||||
|
||||
// return what's going to be a success
|
||||
return validate_error;
|
||||
}
|
||||
|
||||
DPMError ModuleLoader::execute_module( const std::string& module_name, const std::string& command ) const
|
||||
DPMErrorCategory ModuleLoader::execute_module( const std::string& module_name, const std::string& command ) const
|
||||
{
|
||||
// declare a module_handle
|
||||
void * module_handle;
|
||||
|
||||
// attempt to load the module
|
||||
DPMError load_error = load_module( module_name, module_handle );
|
||||
if ( load_error != DPMError::SUCCESS ) {
|
||||
DPMErrorCategory load_error = load_module( module_name, module_handle );
|
||||
if ( load_error != DPMErrorCategory::SUCCESS ) {
|
||||
return load_error;
|
||||
}
|
||||
|
||||
|
@ -85,7 +219,7 @@ DPMError ModuleLoader::execute_module( const std::string& module_name, const std
|
|||
const char* pre_error = dlerror();
|
||||
if ( pre_error != nullptr ) {
|
||||
dlclose( module_handle );
|
||||
return DPMError::UNDEFINED_ERROR;
|
||||
return DPMErrorCategory::UNDEFINED_ERROR;
|
||||
}
|
||||
|
||||
// declare a function pointer type to hold the module symbol to execute
|
||||
|
@ -98,13 +232,13 @@ DPMError ModuleLoader::execute_module( const std::string& module_name, const std
|
|||
const char * dlsym_error = dlerror();
|
||||
if ( dlsym_error != nullptr ) {
|
||||
dlclose( module_handle );
|
||||
return DPMError::SYMBOL_NOT_FOUND;
|
||||
return DPMErrorCategory::SYMBOL_NOT_FOUND;
|
||||
}
|
||||
|
||||
// check if the void pointer was populated
|
||||
if ( execute_fn == nullptr ) {
|
||||
dlclose( module_handle );
|
||||
return DPMError::SYMBOL_NOT_FOUND;
|
||||
return DPMErrorCategory::SYMBOL_NOT_FOUND;
|
||||
}
|
||||
|
||||
// execute the symbol that was loaded and supply the command string being routed from DPM
|
||||
|
@ -115,26 +249,26 @@ DPMError ModuleLoader::execute_module( const std::string& module_name, const std
|
|||
|
||||
// if the result of execution was not 0, return an error
|
||||
if ( exec_error != 0 ) {
|
||||
return DPMError::SYMBOL_EXECUTION_FAILED;
|
||||
return DPMErrorCategory::SYMBOL_EXECUTION_FAILED;
|
||||
}
|
||||
|
||||
// if we made it here, assume it was successful
|
||||
return DPMError::SUCCESS;
|
||||
return DPMErrorCategory::SUCCESS;
|
||||
}
|
||||
|
||||
DPMError ModuleLoader::get_module_version( void * module_handle, std::string& version ) const
|
||||
DPMErrorCategory ModuleLoader::get_module_version( void * module_handle, std::string& version ) const
|
||||
{
|
||||
// validate that the module is even loaded
|
||||
if ( !module_handle ) {
|
||||
version = "DPM ERROR";
|
||||
return DPMError::MODULE_NOT_LOADED;
|
||||
return DPMErrorCategory::MODULE_NOT_LOADED;
|
||||
}
|
||||
|
||||
// Clear any previous error state and handle any residual failure
|
||||
const char* pre_error = dlerror();
|
||||
if ( pre_error != nullptr ) {
|
||||
version = pre_error;
|
||||
return DPMError::UNDEFINED_ERROR;
|
||||
return DPMErrorCategory::UNDEFINED_ERROR;
|
||||
}
|
||||
|
||||
// declare a function pointer type to hold the module symbol to execute
|
||||
|
@ -147,13 +281,13 @@ DPMError ModuleLoader::get_module_version( void * module_handle, std::string& ve
|
|||
const char* error = dlerror();
|
||||
if (error != nullptr) {
|
||||
version = error;
|
||||
return DPMError::SYMBOL_NOT_FOUND;
|
||||
return DPMErrorCategory::SYMBOL_NOT_FOUND;
|
||||
}
|
||||
|
||||
// check if the void pointer was populated
|
||||
if ( version_fn == nullptr ) {
|
||||
version = "ERROR";
|
||||
return DPMError::SYMBOL_NOT_FOUND;
|
||||
return DPMErrorCategory::SYMBOL_NOT_FOUND;
|
||||
}
|
||||
|
||||
// execute the loaded symbol
|
||||
|
@ -162,26 +296,29 @@ DPMError ModuleLoader::get_module_version( void * module_handle, std::string& ve
|
|||
// check the return, and throw an error if it's a null value
|
||||
if ( ver == nullptr ) {
|
||||
version = "MODULE ERROR";
|
||||
return DPMError::INVALID_MODULE;
|
||||
return DPMErrorCategory::INVALID_MODULE;
|
||||
}
|
||||
|
||||
// Set the version string with the result
|
||||
version = ver;
|
||||
|
||||
// if you made it here, assume success
|
||||
return DPMError::SUCCESS;
|
||||
return DPMErrorCategory::SUCCESS;
|
||||
}
|
||||
|
||||
DPMError ModuleLoader::get_module_description( void * module_handle, std::string& description ) const
|
||||
DPMErrorCategory ModuleLoader::get_module_description( void * module_handle, std::string& description ) const
|
||||
{
|
||||
// validate that the module is even loaded
|
||||
if (!module_handle) {
|
||||
description = "DPM ERROR";
|
||||
return DPMError::MODULE_NOT_LOADED;
|
||||
return DPMErrorCategory::MODULE_NOT_LOADED;
|
||||
}
|
||||
|
||||
// Clear any previous error state and handle any residual failure
|
||||
const char* pre_error = dlerror();
|
||||
if ( pre_error != nullptr ) {
|
||||
description = pre_error;
|
||||
return DPMError::UNDEFINED_ERROR;
|
||||
return DPMErrorCategory::UNDEFINED_ERROR;
|
||||
}
|
||||
|
||||
// declare a function pointer type to hold the module symbol to execute
|
||||
|
@ -191,16 +328,16 @@ DPMError ModuleLoader::get_module_description( void * module_handle, std::string
|
|||
DescriptionFn description_fn = (DescriptionFn) dlsym( module_handle, "dpm_get_description" );
|
||||
|
||||
// check for errors from dlsym
|
||||
const char* error = dlerror();
|
||||
const char * error = dlerror();
|
||||
if ( error != nullptr ) {
|
||||
description = "ERROR";
|
||||
return DPMError::SYMBOL_NOT_FOUND;
|
||||
return DPMErrorCategory::SYMBOL_NOT_FOUND;
|
||||
}
|
||||
|
||||
// check if the void pointer was populated
|
||||
if ( description_fn == nullptr ) {
|
||||
description = "ERROR";
|
||||
return DPMError::INVALID_MODULE;
|
||||
return DPMErrorCategory::INVALID_MODULE;
|
||||
}
|
||||
|
||||
// execute the loaded symbol
|
||||
|
@ -209,18 +346,21 @@ DPMError ModuleLoader::get_module_description( void * module_handle, std::string
|
|||
// check the return, and throw an error if it's a null value
|
||||
if ( desc == nullptr ) {
|
||||
description = "MODULE ERROR";
|
||||
return DPMError::INVALID_MODULE;
|
||||
return DPMErrorCategory::INVALID_MODULE;
|
||||
}
|
||||
|
||||
// Set the description string with the result
|
||||
description = desc;
|
||||
|
||||
// if you made it here, assume success
|
||||
return DPMError::SUCCESS;
|
||||
return DPMErrorCategory::SUCCESS;
|
||||
}
|
||||
|
||||
DPMError ModuleLoader::validate_module_interface( void* module_handle, std::vector<std::string>& missing_symbols ) const
|
||||
DPMErrorCategory ModuleLoader::validate_module_interface( void* module_handle, std::vector<std::string>& missing_symbols ) const
|
||||
{
|
||||
// validate that the module is even loaded
|
||||
if ( !module_handle ) {
|
||||
return DPMError::MODULE_NOT_LOADED;
|
||||
return DPMErrorCategory::MODULE_NOT_LOADED;
|
||||
}
|
||||
|
||||
// ensure our starting point of missing symbols is empty
|
||||
|
@ -232,7 +372,7 @@ DPMError ModuleLoader::validate_module_interface( void* module_handle, std::vect
|
|||
// check for any residual lingering errors
|
||||
const char * pre_error = dlerror();
|
||||
if ( pre_error != nullptr ) {
|
||||
return DPMError::UNDEFINED_ERROR;
|
||||
return DPMErrorCategory::UNDEFINED_ERROR;
|
||||
}
|
||||
|
||||
// declare a function pointer type to hold the module symbol to execute
|
||||
|
@ -255,9 +395,9 @@ DPMError ModuleLoader::validate_module_interface( void* module_handle, std::vect
|
|||
|
||||
// if there are no missing symbols, return successfully -- the module has a valid API
|
||||
if ( missing_symbols.empty() ) {
|
||||
return DPMError::SUCCESS;
|
||||
return DPMErrorCategory::SUCCESS;
|
||||
}
|
||||
|
||||
// if not successful, the module's API is invalid and return the appropriate error code
|
||||
return DPMError::INVALID_MODULE;
|
||||
return DPMErrorCategory::INVALID_MODULE;
|
||||
}
|
||||
|
|
54
src/dpm.cpp
54
src/dpm.cpp
|
@ -1,8 +1,40 @@
|
|||
/**
|
||||
* @file dpm.cpp
|
||||
* @brief Main entry point for the Dark Horse Package Manager (DPM)
|
||||
*
|
||||
* Implements the core command-line interface and module routing functionality
|
||||
* for the DPM utility, handling argument parsing, module loading, and execution.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 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/>.
|
||||
*
|
||||
* For bug reports or contributions, please contact the dhlp-contributors
|
||||
* mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <handlers.hpp>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "ModuleLoader.hpp"
|
||||
#include "dpm_interface.hpp"
|
||||
#include "dpm_interface_helpers.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
/*
|
||||
* DPM serves three functions:
|
||||
|
@ -17,7 +49,16 @@ int default_behavior(const ModuleLoader& loader)
|
|||
return main_list_modules(loader);
|
||||
}
|
||||
|
||||
// entry point for the DPM utility
|
||||
/**
|
||||
* @brief Entry point for the DPM utility
|
||||
*
|
||||
* Processes command-line arguments, loads and executes the appropriate module,
|
||||
* and handles any errors that occur during execution.
|
||||
*
|
||||
* @param argc Number of command-line arguments
|
||||
* @param argv Array of C-style strings containing the arguments
|
||||
* @return Exit code indicating success (0) or failure (non-zero)
|
||||
*/
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
// process the arguments suppplied to DPM and provide
|
||||
|
@ -43,8 +84,15 @@ int main( int argc, char* argv[] )
|
|||
}
|
||||
|
||||
// execute the module
|
||||
DPMError execute_error = loader.execute_module(args.module_name, args.command);
|
||||
DPMErrorCategory execute_error = loader.execute_module(args.module_name, args.command);
|
||||
|
||||
std::string extracted_path;
|
||||
loader.get_module_path(extracted_path);
|
||||
|
||||
FlexDPMError result = make_error(execute_error);
|
||||
result.module_name = args.module_name.c_str();
|
||||
result.module_path = extracted_path.c_str();
|
||||
|
||||
// pair result with a message and exit with the appropriate error code
|
||||
return print_error( execute_error, args.module_name, args.module_path );
|
||||
return handle_error( result );
|
||||
}
|
|
@ -1,14 +1,34 @@
|
|||
#include "dpm_interface.hpp"
|
||||
|
||||
/*
|
||||
* DPM Interface methods.
|
||||
*
|
||||
* DPM Interface methods. These are wrappers of DPM functionality that are meant to handle user view, turning
|
||||
* error codes into human-presentable information, etc. Features are defined internally, these will only ever be
|
||||
* wrappers of existing features to provide the human/cli interface.
|
||||
* These are for entry points for the DPM cli.
|
||||
*
|
||||
* These are wrappers of DPM functionality that are meant to handle user
|
||||
* view, turning error codes into human-presentable information, etc.
|
||||
*
|
||||
* Also includes helpers related to the CLI.
|
||||
*/
|
||||
|
||||
// check if the module path exists
|
||||
/**
|
||||
* Verify that the module path exists and is accessible.
|
||||
*
|
||||
* This function checks if the configured module path exists, is a directory,
|
||||
* and has the necessary read permissions.
|
||||
*
|
||||
* @param loader Reference to a ModuleLoader object that provides the module path
|
||||
*
|
||||
* @return 0 if the path exists and is accessible, 1 otherwise
|
||||
*
|
||||
* The function performs the following checks:
|
||||
* 1. Retrieves the module path from the loader
|
||||
* 2. Verifies that the path exists in the filesystem
|
||||
* 3. Confirms that the path is a directory
|
||||
* 4. Checks that the directory has read permissions
|
||||
*
|
||||
* If any check fails, an appropriate error message is displayed to stderr.
|
||||
*/
|
||||
int main_check_module_path(const ModuleLoader& loader)
|
||||
{
|
||||
std::string path;
|
||||
|
@ -38,20 +58,46 @@ int main_check_module_path(const ModuleLoader& loader)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// list the modules
|
||||
/**
|
||||
* List all available and valid DPM modules.
|
||||
*
|
||||
* This function retrieves and displays a formatted table of available DPM modules
|
||||
* from the specified module path, including their versions and descriptions.
|
||||
*
|
||||
* @param loader Reference to a ModuleLoader object that provides access to modules
|
||||
*
|
||||
* @return 0 on success, 1 on failure
|
||||
*
|
||||
* The function performs the following operations:
|
||||
* 1. Gets the configured module path from the loader
|
||||
* 2. Retrieves a list of all potential modules in that path
|
||||
* 3. Validates each module by checking for required symbols
|
||||
* 4. Collects version and description information from valid modules
|
||||
* 5. Formats and displays the information in a tabular format
|
||||
*
|
||||
* If no modules are found or if no valid modules are found, appropriate
|
||||
* messages are displayed.
|
||||
*
|
||||
* Modules are considered valid if they expose all required interface
|
||||
* symbols as defined in module_interface.hpp.
|
||||
*/
|
||||
int main_list_modules(const ModuleLoader& loader)
|
||||
{
|
||||
// initialize an empty modules list
|
||||
std::vector<std::string> modules;
|
||||
|
||||
// initialize an empty path
|
||||
std::string path;
|
||||
|
||||
DPMError get_path_error = loader.get_module_path(path);
|
||||
if (get_path_error != DPMError::SUCCESS) {
|
||||
// set the module path
|
||||
DPMErrorCategory get_path_error = loader.get_module_path(path);
|
||||
if ( get_path_error != DPMErrorCategory::SUCCESS ) {
|
||||
std::cerr << "Failed to get module path" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
DPMError list_error = loader.list_available_modules(modules);
|
||||
if (list_error != DPMError::SUCCESS) {
|
||||
DPMErrorCategory list_error = loader.list_available_modules(modules);
|
||||
if (list_error != DPMErrorCategory::SUCCESS) {
|
||||
std::cerr << "No modules found in: " << path << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
@ -61,17 +107,18 @@ int main_list_modules(const ModuleLoader& loader)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// First pass: Identify valid modules
|
||||
std::vector<std::string> valid_modules;
|
||||
for (int i = 0; i < modules.size(); i++) {
|
||||
void* handle;
|
||||
DPMError load_error = loader.load_module(modules[i], handle);
|
||||
if (load_error != DPMError::SUCCESS) {
|
||||
DPMErrorCategory load_error = loader.load_module(modules[i], handle);
|
||||
if (load_error != DPMErrorCategory::SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> missing_symbols;
|
||||
DPMError validate_error = loader.validate_module_interface(handle, missing_symbols);
|
||||
if (validate_error == DPMError::SUCCESS) {
|
||||
DPMErrorCategory validate_error = loader.validate_module_interface(handle, missing_symbols);
|
||||
if (validate_error == DPMErrorCategory::SUCCESS) {
|
||||
valid_modules.push_back(modules[i]);
|
||||
}
|
||||
dlclose(handle);
|
||||
|
@ -82,130 +129,53 @@ int main_list_modules(const ModuleLoader& loader)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Second pass: Collect module information and calculate column widths
|
||||
size_t max_name_length = 0;
|
||||
size_t max_version_length = 0;
|
||||
std::vector<std::string> versions(valid_modules.size(), "unknown");
|
||||
std::vector<std::string> descriptions(valid_modules.size(), "unknown");
|
||||
|
||||
for (int i = 0; i < valid_modules.size(); i++) {
|
||||
void* module_handle;
|
||||
std::string version;
|
||||
max_name_length = std::max(max_name_length, valid_modules[i].length());
|
||||
|
||||
DPMError load_error = loader.load_module(valid_modules[i], module_handle);
|
||||
if (load_error == DPMError::SUCCESS) {
|
||||
DPMError version_error = loader.get_module_version(module_handle, version);
|
||||
if (version_error == DPMError::SUCCESS) {
|
||||
DPMErrorCategory load_error = loader.load_module(valid_modules[i], module_handle);
|
||||
if (load_error == DPMErrorCategory::SUCCESS) {
|
||||
// Get version
|
||||
std::string version = "unknown";
|
||||
DPMErrorCategory version_error = loader.get_module_version(module_handle, version);
|
||||
if (version_error == DPMErrorCategory::SUCCESS) {
|
||||
versions[i] = version;
|
||||
max_version_length = std::max(max_version_length, version.length());
|
||||
}
|
||||
|
||||
// Get description
|
||||
std::string description = "unknown";
|
||||
DPMErrorCategory desc_error = loader.get_module_description(module_handle, description);
|
||||
if (desc_error == DPMErrorCategory::SUCCESS) {
|
||||
descriptions[i] = description;
|
||||
}
|
||||
|
||||
dlclose(module_handle);
|
||||
}
|
||||
}
|
||||
|
||||
const int column_spacing = 4;
|
||||
|
||||
// Display the table header
|
||||
std::cout << "\nAvailable modules in '" << path << "':" << std::endl << std::endl;
|
||||
std::cout << std::left << std::setw(max_name_length + column_spacing) << "MODULE"
|
||||
<< std::setw(max_version_length + column_spacing) << "VERSION"
|
||||
<< "DESCRIPTION" << std::endl;
|
||||
|
||||
// Display the table rows
|
||||
for (int i = 0; i < valid_modules.size(); i++) {
|
||||
void* module_handle;
|
||||
std::string version = "unknown";
|
||||
std::string description = "unknown";
|
||||
|
||||
DPMError load_error = loader.load_module(valid_modules[i], module_handle);
|
||||
if (load_error == DPMError::SUCCESS) {
|
||||
DPMError version_error = loader.get_module_version(module_handle, version);
|
||||
DPMError desc_error = loader.get_module_description(module_handle, description);
|
||||
dlclose(module_handle);
|
||||
}
|
||||
|
||||
std::cout << std::left << std::setw(max_name_length + column_spacing) << valid_modules[i]
|
||||
<< std::setw(max_version_length + column_spacing) << version
|
||||
<< description << std::endl;
|
||||
<< std::setw(max_version_length + column_spacing) << versions[i]
|
||||
<< descriptions[i] << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CommandArgs parse_args(int argc, char* argv[])
|
||||
{
|
||||
CommandArgs args;
|
||||
args.module_path = "/usr/lib/dpm/modules/"; // Set to same default as ModuleLoader
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"module-path", required_argument, 0, 'm'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int opt;
|
||||
int option_index = 0;
|
||||
while ((opt = getopt_long(argc, argv, "m:h", long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'm':
|
||||
args.module_path = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
std::cout << "Usage: dpm [options] [module-name] [module args...]\n\n"
|
||||
<< "Options:\n\n"
|
||||
<< " -m, --module-path PATH Path to DPM modules\n"
|
||||
<< " -h, --help Show this help message\n\n"
|
||||
<< "If no module is specified, available modules will be listed.\n\n";
|
||||
exit(0);
|
||||
case '?':
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
args.module_name = argv[optind++];
|
||||
|
||||
for (int i = optind; i < argc; i++) {
|
||||
if (!args.command.empty()) {
|
||||
args.command += " ";
|
||||
}
|
||||
|
||||
std::string arg = argv[i];
|
||||
if (arg.find(' ') != std::string::npos) {
|
||||
args.command += "\"" + arg + "\"";
|
||||
} else {
|
||||
args.command += arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
int print_error(DPMError error, const std::string& module_name, const std::string& module_path) {
|
||||
switch (error) {
|
||||
case DPMError::SUCCESS:
|
||||
return 0;
|
||||
case DPMError::PATH_NOT_FOUND:
|
||||
std::cerr << "Module path not found: " << module_path << std::endl;
|
||||
return 1;
|
||||
case DPMError::PATH_NOT_DIRECTORY:
|
||||
std::cerr << "Module path is not a directory: " << module_path << std::endl;
|
||||
return 1;
|
||||
case DPMError::PERMISSION_DENIED:
|
||||
std::cerr << "Permission denied accessing module: " << module_name << std::endl;
|
||||
return 1;
|
||||
case DPMError::MODULE_NOT_FOUND:
|
||||
std::cerr << "Module not found: " << module_name << std::endl;
|
||||
return 1;
|
||||
case DPMError::MODULE_NOT_LOADED:
|
||||
std::cerr << "Attempted to execute module before loading it: " << module_name << std::endl;
|
||||
return 1;
|
||||
case DPMError::MODULE_LOAD_FAILED:
|
||||
std::cerr << "Failed to load module: " << module_name << std::endl;
|
||||
return 1;
|
||||
case DPMError::INVALID_MODULE:
|
||||
std::cerr << "Invalid module format: " << module_name << std::endl;
|
||||
return 1;
|
||||
case DPMError::UNDEFINED_ERROR:
|
||||
std::cerr << "Undefined error occurred with module: " << module_name << std::endl;
|
||||
return 1;
|
||||
default:
|
||||
std::cerr << "Unknown error executing module: " << module_name << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
#include "dpm_interface_helpers.hpp"
|
||||
|
||||
/**
|
||||
* Parse command line arguments for DPM.
|
||||
*
|
||||
* This function parses the command line arguments provided to DPM
|
||||
* and builds a CommandArgs structure containing the parsed values.
|
||||
*
|
||||
* @param argc The number of arguments provided to the program
|
||||
* @param argv Array of C-style strings containing the arguments
|
||||
*
|
||||
* @return CommandArgs structure containing the parsed command line arguments
|
||||
*
|
||||
* The function handles the following arguments:
|
||||
* - ``-m, --module-path PATH``: Sets the directory path where DPM modules are located
|
||||
* - ``-h, --help``: Displays a help message and exits
|
||||
*
|
||||
* Additional arguments are processed as follows:
|
||||
* - First non-option argument is treated as the module name
|
||||
* - All remaining arguments are combined into a single command string for the module
|
||||
*
|
||||
* If the argument contains spaces, it will be quoted in the command string.
|
||||
*
|
||||
* If no module name is provided, the module_name field will be empty.
|
||||
*/
|
||||
CommandArgs parse_args(int argc, char* argv[])
|
||||
{
|
||||
CommandArgs args;
|
||||
args.module_path = "/usr/lib/dpm/modules/"; // Set to same default as ModuleLoader
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"module-path", required_argument, 0, 'm'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int opt;
|
||||
int option_index = 0;
|
||||
while ((opt = getopt_long(argc, argv, "m:h", long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'm':
|
||||
args.module_path = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
std::cout << "Usage: dpm [options] [module-name] [module args...]\n\n"
|
||||
<< "Options:\n\n"
|
||||
<< " -m, --module-path PATH Path to DPM modules\n"
|
||||
<< " -h, --help Show this help message\n\n"
|
||||
<< "If no module is specified, available modules will be listed.\n\n";
|
||||
exit(0);
|
||||
case '?':
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
args.module_name = argv[optind++];
|
||||
|
||||
for (int i = optind; i < argc; i++) {
|
||||
if (!args.command.empty()) {
|
||||
args.command += " ";
|
||||
}
|
||||
|
||||
std::string arg = argv[i];
|
||||
if (arg.find(' ') != std::string::npos) {
|
||||
args.command += "\"" + arg + "\"";
|
||||
} else {
|
||||
args.command += arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#include "error.hpp"
|
||||
|
||||
// Simple helper function that takes only the required error category
|
||||
FlexDPMError make_error(DPMErrorCategory error_category)
|
||||
{
|
||||
// Create an empty error struct
|
||||
FlexDPMError error;
|
||||
|
||||
// Set the error category
|
||||
error.error = error_category;
|
||||
|
||||
// Initialize the other fields to NULL
|
||||
error.module_name = NULL;
|
||||
error.module_path = NULL;
|
||||
error.message = NULL;
|
||||
|
||||
// let the consumer populate any other fields they want with `self.field_name = whatever`.
|
||||
return error;
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
#include "handlers.hpp"
|
||||
|
||||
// Helper function for validating required fields in a FlexDPMError
|
||||
void validate_field(FlexDPMError context, const char* field_name, const void* field_value)
|
||||
{
|
||||
if (!field_value) {
|
||||
std::cerr << "Error: Incomplete error context. Missing required field: " << field_name;
|
||||
std::cerr << " (Error category: " << static_cast<int>(context.error) << ")" << std::endl;
|
||||
|
||||
// Hard exit when a required field is missing
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Main error handler that dispatches to specific handlers
|
||||
int handle_error(FlexDPMError context) {
|
||||
if (context.error == DPMErrorCategory::SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (context.error) {
|
||||
case DPMErrorCategory::PATH_NOT_FOUND:
|
||||
validate_field(context, "module_path", context.module_path);
|
||||
return handle_path_not_found(context);
|
||||
|
||||
case DPMErrorCategory::PATH_NOT_DIRECTORY:
|
||||
validate_field(context, "module_path", context.module_path);
|
||||
return handle_path_not_directory(context);
|
||||
|
||||
case DPMErrorCategory::PATH_TOO_LONG:
|
||||
validate_field(context, "module_path", context.module_path);
|
||||
return handle_path_too_long(context);
|
||||
|
||||
case DPMErrorCategory::PERMISSION_DENIED:
|
||||
validate_field(context, "module_path", context.module_path);
|
||||
return handle_permission_denied(context);
|
||||
|
||||
case DPMErrorCategory::MODULE_NOT_FOUND:
|
||||
validate_field(context, "module_name", context.module_name);
|
||||
validate_field(context, "module_path", context.module_path);
|
||||
return handle_module_not_found(context);
|
||||
|
||||
case DPMErrorCategory::MODULE_NOT_LOADED:
|
||||
validate_field(context, "module_name", context.module_name);
|
||||
return handle_module_not_loaded(context);
|
||||
|
||||
case DPMErrorCategory::MODULE_LOAD_FAILED:
|
||||
validate_field(context, "module_name", context.module_name);
|
||||
return handle_module_load_failed(context);
|
||||
|
||||
case DPMErrorCategory::INVALID_MODULE:
|
||||
validate_field(context, "module_name", context.module_name);
|
||||
return handle_invalid_module(context);
|
||||
|
||||
case DPMErrorCategory::SYMBOL_NOT_FOUND:
|
||||
validate_field(context, "module_name", context.module_name);
|
||||
return handle_symbol_not_found(context);
|
||||
|
||||
case DPMErrorCategory::SYMBOL_EXECUTION_FAILED:
|
||||
validate_field(context, "module_name", context.module_name);
|
||||
return handle_symbol_execution_failed(context);
|
||||
|
||||
case DPMErrorCategory::UNDEFINED_ERROR:
|
||||
return handle_undefined_error(context);
|
||||
|
||||
default:
|
||||
std::cerr << "Error: Unknown error code" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now the individual handlers can be simplified since required fields are guaranteed
|
||||
int handle_path_not_found( FlexDPMError context ) {
|
||||
std::cerr << "Fatal error: The module directory '" << context.module_path << "' was not found. Exiting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_path_not_directory( FlexDPMError context ) {
|
||||
std::cerr << "Fatal error: The module path '" << context.module_path << "' is not a directory. Exiting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_path_too_long( FlexDPMError context ) {
|
||||
std::cerr << "Error: Module path is too long: '" << context.module_path << "'. Exiting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_permission_denied( FlexDPMError context ) {
|
||||
std::cerr << "Error: Permission denied accessing the modules path: '" << context.module_path << "'. Exiting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_module_not_found( FlexDPMError context ) {
|
||||
std::cerr << "Error: Module '"<< context.module_name << "' not found in '" << context.module_path << "'. Exiting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_module_not_loaded( FlexDPMError context ) {
|
||||
std::cerr << "Error: Attempted to execute module before loading it: " << context.module_name << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_module_load_failed( FlexDPMError context ) {
|
||||
std::cerr << "Error: Failed to load module: " << context.module_name << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_invalid_module( FlexDPMError context ) {
|
||||
std::cerr << "Error: Invalid module format: " << context.module_name << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_symbol_not_found( FlexDPMError context ) {
|
||||
std::cerr << "Error: Symbol not found in module: " << context.module_name;
|
||||
if (context.message) {
|
||||
std::cerr << " (" << context.message << ")";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_symbol_execution_failed(FlexDPMError context) {
|
||||
std::cerr << "Error: Module execution failed: " << context.module_name << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_undefined_error(FlexDPMError context) {
|
||||
std::cerr << "Error: Undefined error occurred";
|
||||
if (context.module_name) {
|
||||
std::cerr << " with module: " << context.module_name;
|
||||
}
|
||||
if (context.message) {
|
||||
std::cerr << " (" << context.message << ")";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue