From 75ee779b642732a78a0d42174f31450afd635909 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 31 Oct 2025 03:58:59 +0900 Subject: [PATCH 1/3] Feat: adding --ipc and --ipc-path command line --- main.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 6 deletions(-) diff --git a/main.cpp b/main.cpp index a829c44..5485910 100644 --- a/main.cpp +++ b/main.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -39,6 +41,8 @@ namespace ctrace argManager.addOption("--entry-points", true, 'e'); argManager.addOption("--report-file", true, 'r'); argManager.addOption("--async", false, 'a'); + argManager.addOption("--ipc", true, 'p'); + argManager.addOption("--ipc-path", true, 't'); // Parsing des arguments argManager.parse(argc, argv); @@ -82,26 +86,132 @@ int main(int argc, char *argv[]) << ctrace::Color::YELLOW << config.global.entry_points << ctrace::Color::RESET << std::endl; - // TODO : handle multi-file parsing - for (const auto& file : config.files) + std::vector sourceFiles; + sourceFiles.reserve(config.files.size()); + + const auto appendResolved = [&](const std::string& candidate, + const std::filesystem::path& baseDir) -> bool + { + if (candidate.empty()) + { + return false; + } + std::filesystem::path resolved(candidate); + if (resolved.is_relative() && !baseDir.empty()) + { + resolved = baseDir / resolved; + } + resolved = resolved.lexically_normal(); + sourceFiles.emplace_back(resolved.string()); + return true; + }; + + for (const auto& fileConfig : config.files) + { + const std::string& entry = fileConfig.src_file; + + bool expanded = false; + if (!entry.empty() && (entry.ends_with(".json") || entry.ends_with(".JSON"))) + { + std::ifstream manifestStream(entry); + if (manifestStream.is_open()) + { + std::ostringstream buffer; + buffer << manifestStream.rdbuf(); + + const auto manifest = json::parse(buffer.str(), nullptr, false); + if (!manifest.is_discarded()) + { + const std::filesystem::path manifestDir = std::filesystem::path(entry).parent_path(); + + const auto appendFromArray = [&](const json& arr) -> bool + { + bool appended = false; + for (const auto& item : arr) + { + if (item.is_string()) + { + appended |= appendResolved(item.get(), manifestDir); + } + else if (item.is_object()) + { + if (const auto it = item.find("file"); it != item.end() && it->is_string()) + { + appended |= appendResolved(it->get(), manifestDir); + } + else if (const auto itSrc = item.find("src_file"); itSrc != item.end() && itSrc->is_string()) + { + appended |= appendResolved(itSrc->get(), manifestDir); + } + else if (const auto itPath = item.find("path"); itPath != item.end() && itPath->is_string()) + { + appended |= appendResolved(itPath->get(), manifestDir); + } + } + } + return appended; + }; + + if (manifest.is_array()) + { + expanded = appendFromArray(manifest); + } + else if (manifest.is_object()) + { + if (const auto it = manifest.find("files"); it != manifest.end() && it->is_array()) + { + expanded = appendFromArray(*it); + } + else if (const auto it = manifest.find("sources"); it != manifest.end() && it->is_array()) + { + expanded = appendFromArray(*it); + } + else if (const auto it = manifest.find("compile_commands"); it != manifest.end() && it->is_array()) + { + expanded = appendFromArray(*it); + } + else if (const auto itFile = manifest.find("file"); itFile != manifest.end() && itFile->is_string()) + { + expanded = appendResolved(itFile->get(), manifestDir); + } + else if (const auto itSrc = manifest.find("src_file"); itSrc != manifest.end() && itSrc->is_string()) + { + expanded = appendResolved(itSrc->get(), manifestDir); + } + else if (const auto itPath = manifest.find("path"); itPath != manifest.end() && itPath->is_string()) + { + expanded = appendResolved(itPath->get(), manifestDir); + } + } + } + } + } + + if (!expanded) + { + sourceFiles.emplace_back(entry); + } + } + + for (const auto& file : sourceFiles) { std::cout << ctrace::Color::CYAN << "File: " - << ctrace::Color::YELLOW << file.src_file << ctrace::Color::RESET << std::endl; + << ctrace::Color::YELLOW << file << ctrace::Color::RESET << std::endl; if (config.global.hasStaticAnalysis) { std::cout << ctrace::Color::CYAN << "Running static analysis..." << ctrace::Color::RESET << std::endl; - invoker.runStaticTools(file.src_file); + invoker.runStaticTools(file); } if (config.global.hasDynamicAnalysis) { std::cout << ctrace::Color::CYAN << "Running dynamic analysis..." << ctrace::Color::RESET << std::endl; - invoker.runDynamicTools(file.src_file); + invoker.runDynamicTools(file); } if (config.global.hasInvokedSpecificTools) { std::cout << ctrace::Color::CYAN << "Running specific tools..." << ctrace::Color::RESET << std::endl; - invoker.runSpecificTools(config.global.specificTools, file.src_file); + invoker.runSpecificTools(config.global.specificTools, file); } } return 0; From a3ac0674b7127d9e1c4187f2bfad18144e7eab1d Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 31 Oct 2025 03:59:59 +0900 Subject: [PATCH 2/3] Feat: adding socket ipc --- include/Config/config.hpp | 30 ++++++++++- include/Process/Ipc/IpcStrategy.hpp | 59 +++++++++++++++++++++ include/Process/Tools/AnalysisTools.hpp | 33 ++++++++---- include/Process/Tools/AnalysisToolsBase.hpp | 16 ++++++ include/Process/Tools/IAnalysisTools.hpp | 9 ++++ include/Process/Tools/ToolsInvoker.hpp | 26 ++++++--- include/ctrace_defs/types.hpp | 10 ++++ 7 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 include/Process/Ipc/IpcStrategy.hpp create mode 100644 include/Process/Tools/AnalysisToolsBase.hpp diff --git a/include/Config/config.hpp b/include/Config/config.hpp index b906fa5..dca601c 100644 --- a/include/Config/config.hpp +++ b/include/Config/config.hpp @@ -9,6 +9,8 @@ #include "ArgumentParser/ArgumentManager.hpp" #include "ArgumentParser/ArgumentParserFactory.hpp" #include "ctrace_tools/strings.hpp" +#include "Process/Ipc/IpcStrategy.hpp" +#include "ctrace_defs/types.hpp" static void printHelp(void) { @@ -29,6 +31,9 @@ static void printHelp(void) --invoke Invokes specific tools (comma-separated). Available tools: flawfinder, ikos, cppcheck, tscancode. --input Specifies the source files to analyse (comma-separated). + --ipc Specifies the IPC method to use (e.g., fifo, socket). + --ipc-path Specifies the IPC path (default: /tmp/coretrace_ipc). + --async Enables asynchronous execution. Examples: ctrace --input main.cpp,util.cpp --static --invoke=cppcheck,flawfinder @@ -67,11 +72,13 @@ namespace ctrace struct GlobalConfig { bool verbose = false; ///< Enables verbose output. - std:: launch hasAsync = std::launch::deferred; ///< Enables asynchronous execution. + std::launch hasAsync = std::launch::deferred; ///< Enables asynchronous execution. bool hasSarifFormat = false; ///< Indicates if SARIF format is enabled. bool hasStaticAnalysis = false; ///< Indicates if static analysis is enabled. bool hasDynamicAnalysis = false; ///< Indicates if dynamic analysis is enabled. bool hasInvokedSpecificTools = false; ///< Indicates if specific tools are invoked. + std::string ipc = ctrace_defs::IPC_TYPES.front(); ///< IPC method to use (e.g., fifo, socket). + std::string ipcPath = "/tmp/coretrace_ipc"; ///< Path for IPC communication. std::vector specificTools; ///< List of specific tools to invoke. @@ -180,6 +187,25 @@ namespace ctrace { config.global.entry_points = value; }; + commands["--ipc"] = [this](const std::string& value) + { + auto ipc_list = ctrace_defs::IPC_TYPES; + + if (std::find(ipc_list.begin(), ipc_list.end(), value) == ipc_list.end()) + { + std::cerr << "Invalid IPC type: '" << value << "'\n" + << "Available IPC types: "; + for (const auto& ipc : ipc_list) + std::cerr << ipc << " "; + std::cerr << std::endl; + std::exit(EXIT_FAILURE); + } + config.global.ipc = value; + }; + commands["--ipc-path"] = [this](const std::string& value) + { + config.global.ipcPath = value; + }; // commands["--output"] = [this](const std::string& value) { // if (!config.files.empty()) config.files.back().output_file = value; // }; @@ -207,7 +233,7 @@ namespace ctrace * * @param argManager The argument manager containing parsed options. */ - void process(ArgumentManager& argManager) + void process(ArgumentManager& argManager) { for (const auto& [option, command] : commands) { diff --git a/include/Process/Ipc/IpcStrategy.hpp b/include/Process/Ipc/IpcStrategy.hpp new file mode 100644 index 0000000..fd0995f --- /dev/null +++ b/include/Process/Ipc/IpcStrategy.hpp @@ -0,0 +1,59 @@ +#ifndef IPC_STRATEGY_HPP +#define IPC_STRATEGY_HPP + +#include + +#include "../Tools/IAnalysisTools.hpp" +#include "../ProcessFactory.hpp" +#include "../ThreadProcess.hpp" + +#include +#include +#include +#include +#include +#include + +class IpcStrategy +{ +public: + virtual ~IpcStrategy() = default; + virtual void write(const std::string& data) = 0; + virtual void close() = 0; // Pour cleanup RAII +}; + +class UnixSocketStrategy : public IpcStrategy +{ +private: + int sock; + std::string path; +public: + UnixSocketStrategy(const std::string& socketPath) : path(socketPath), sock(-1) { + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) { + throw std::runtime_error("Erreur création socket: " + std::string(strerror(errno))); + } + sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1); + if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == -1) { + throw std::runtime_error("Erreur connexion socket: " + std::string(strerror(errno))); + } + } + ~UnixSocketStrategy() { close(); } + void write(const std::string& data) override { + if (send(sock, data.c_str(), data.size(), 0) == -1) { + throw std::runtime_error("Erreur envoi socket: " + std::string(strerror(errno))); + } + } + void close() override { + if (sock != -1) { + ::close(sock); + sock = -1; + unlink(path.c_str()); // Optionnel + } + } +}; + +#endif // IPC_STRATEGY_HPP diff --git a/include/Process/Tools/AnalysisTools.hpp b/include/Process/Tools/AnalysisTools.hpp index 9b65ebb..0319c08 100644 --- a/include/Process/Tools/AnalysisTools.hpp +++ b/include/Process/Tools/AnalysisTools.hpp @@ -3,7 +3,8 @@ #include -#include "IAnalysisTools.hpp" +// #include "IAnalysisTools.hpp" +#include "AnalysisToolsBase.hpp" #include "ctrace_tools/languageType.hpp" #include "ctrace_tools/mangle.hpp" #include "../ProcessFactory.hpp" @@ -12,7 +13,8 @@ using json = nlohmann::json; -class EntryPoint { +class EntryPoint +{ public: EntryPoint(const std::string& entryPointName, const std::vector& paramTypes) : name(entryPointName), paramTypes(paramTypes) @@ -74,7 +76,7 @@ namespace ctrace { // Outils statiques -class IkosToolImplementation : public IAnalysisTool +class IkosToolImplementation : public AnalysisToolBase { public: void execute(const std::string& file, ctrace::ProgramConfig config) const override @@ -137,7 +139,7 @@ class IkosToolImplementation : public IAnalysisTool } }; -class FlawfinderToolImplementation : public IAnalysisTool +class FlawfinderToolImplementation : public AnalysisToolBase { public: void execute(const std::string& file, ctrace::ProgramConfig config) const override @@ -161,9 +163,18 @@ class FlawfinderToolImplementation : public IAnalysisTool argsProcess.push_back("--sarif"); } argsProcess.push_back(src_file); - auto process = ProcessFactory::createProcess("python3", argsProcess); // ou "cmd.exe" pour Windows + auto process = ProcessFactory::createProcess("python3", argsProcess); // or "cmd.exe" for Windows process->execute(); - ctrace::Thread::Output::cout(process->logOutput); + + if (config.global.ipc == "standardIO") + { + ctrace::Thread::Output::cout(process->logOutput); + } + else + { + ipc->write(process->logOutput); + } + } catch (const std::exception& e) { ctrace::Thread::Output::cout("Error: " + std::string(e.what())); return; @@ -175,7 +186,7 @@ class FlawfinderToolImplementation : public IAnalysisTool } }; -class TscancodeToolImplementation : public IAnalysisTool +class TscancodeToolImplementation : public AnalysisToolBase { public: void execute(const std::string& file, ProgramConfig config) const override; @@ -187,7 +198,7 @@ class TscancodeToolImplementation : public IAnalysisTool }; -class CppCheckToolImplementation : public IAnalysisTool +class CppCheckToolImplementation : public AnalysisToolBase { public: void execute(const std::string& file, ctrace::ProgramConfig config) const override @@ -222,7 +233,7 @@ class CppCheckToolImplementation : public IAnalysisTool }; // Outils dynamiques -class DynTool1 : public IAnalysisTool +class DynTool1 : public AnalysisToolBase { public: void execute(const std::string& file, ctrace::ProgramConfig config) const override @@ -235,7 +246,7 @@ class DynTool1 : public IAnalysisTool } }; -class DynTool2 : public IAnalysisTool +class DynTool2 : public AnalysisToolBase { public: void execute(const std::string& file, ctrace::ProgramConfig config) const override @@ -248,7 +259,7 @@ class DynTool2 : public IAnalysisTool } }; -class DynTool3 : public IAnalysisTool +class DynTool3 : public AnalysisToolBase { public: void execute(const std::string& file, ctrace::ProgramConfig config) const override diff --git a/include/Process/Tools/AnalysisToolsBase.hpp b/include/Process/Tools/AnalysisToolsBase.hpp new file mode 100644 index 0000000..8a90adb --- /dev/null +++ b/include/Process/Tools/AnalysisToolsBase.hpp @@ -0,0 +1,16 @@ +#include "IAnalysisTools.hpp" + +namespace ctrace +{ + + class AnalysisToolBase : public IAnalysisTool + { + protected: + std::shared_ptr ipc; + public: + void setIpcStrategy(std::shared_ptr strategy) override { + ipc = std::move(strategy); + } + }; + +} \ No newline at end of file diff --git a/include/Process/Tools/IAnalysisTools.hpp b/include/Process/Tools/IAnalysisTools.hpp index ca98218..12190e3 100644 --- a/include/Process/Tools/IAnalysisTools.hpp +++ b/include/Process/Tools/IAnalysisTools.hpp @@ -3,6 +3,7 @@ #include #include "Config/config.hpp" +#include "../Ipc/IpcStrategy.hpp" namespace ctrace { @@ -39,6 +40,14 @@ namespace ctrace { * @return A `std::string` representing the name of the tool. */ virtual std::string name() const = 0; + + /** + * @brief Sets the IPC strategy for the analysis tool. + * This method allows the tool to communicate results or data + * through the specified IPC mechanism. + * @param ipc A shared pointer to an `IpcStrategy` instance. + */ + virtual void setIpcStrategy(std::shared_ptr ipc) = 0; }; } diff --git a/include/Process/Tools/ToolsInvoker.hpp b/include/Process/Tools/ToolsInvoker.hpp index 13080f6..a267027 100644 --- a/include/Process/Tools/ToolsInvoker.hpp +++ b/include/Process/Tools/ToolsInvoker.hpp @@ -95,13 +95,24 @@ class ToolInvoker { tools["dyn_tools_2"] = std::make_unique(); tools["dyn_tools_3"] = std::make_unique(); - - // Groupes prédéfinis static_tools = {"cppcheck", "flawfinder", "tscancode", "ikos"}; dynamic_tools = {"dyn_tools_1", "dyn_tools_2", "dyn_tools_3"}; + if (m_config.global.ipc == "standardIO") + { + m_ipc = nullptr; // Use std::cout directely + std::cout << "\033[36mUsing standardIO for IPC.\033[0m\n"; + } + else + { + m_ipc = std::make_shared(m_config.global.ipcPath); + + for (auto& [_, tool] : tools) + tool->setIpcStrategy(m_ipc); + } } - // Exécute tous les outils statiques + + // execute all static analysis tools void runStaticTools(const std::string& file) const { std::vector> results; @@ -123,9 +134,11 @@ class ToolInvoker { } } - // Exécute tous les outils dynamiques - void runDynamicTools(const std::string& file) const { - for (const auto& tool_name : dynamic_tools) { + // execute all dynamic analysis tools + void runDynamicTools(const std::string& file) const + { + for (const auto& tool_name : dynamic_tools) + { tools.at(tool_name)->execute(file, m_config); } } @@ -153,6 +166,7 @@ class ToolInvoker { ctrace::ProgramConfig m_config; uint8_t m_nbThreadPool; std::launch m_policy; + std::shared_ptr m_ipc; }; } diff --git a/include/ctrace_defs/types.hpp b/include/ctrace_defs/types.hpp index 7b79c8c..722608d 100644 --- a/include/ctrace_defs/types.hpp +++ b/include/ctrace_defs/types.hpp @@ -1,6 +1,9 @@ #ifndef TYPES_HPP #define TYPES_HPP +#include +#include + namespace ctrace_defs { /** @@ -14,6 +17,13 @@ namespace ctrace_defs C = 0, CPP = 1, }; + + inline const std::vector IPC_TYPES = { + "standardIO", // default + "socket", + // "pipe" // future implementation + }; + } #endif // TYPES_HPP From 6cb973b9fb69290af8f4ff9f62104e584b26dc5c Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 31 Oct 2025 04:22:41 +0900 Subject: [PATCH 3/3] Feat: improve system exception --- include/Process/Ipc/IpcStrategy.hpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/include/Process/Ipc/IpcStrategy.hpp b/include/Process/Ipc/IpcStrategy.hpp index fd0995f..c84b8c8 100644 --- a/include/Process/Ipc/IpcStrategy.hpp +++ b/include/Process/Ipc/IpcStrategy.hpp @@ -22,6 +22,16 @@ class IpcStrategy virtual void close() = 0; // Pour cleanup RAII }; +namespace ctrace::ipc +{ + class SocketError : public std::runtime_error + { + public: + explicit SocketError(const std::string& msg) + : std::runtime_error("[IPC::SocketError] " + msg) {} + }; +} + class UnixSocketStrategy : public IpcStrategy { private: @@ -31,20 +41,26 @@ class UnixSocketStrategy : public IpcStrategy UnixSocketStrategy(const std::string& socketPath) : path(socketPath), sock(-1) { sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { - throw std::runtime_error("Erreur création socket: " + std::string(strerror(errno))); + throw std::runtime_error("Error socket creation: " + std::string(strerror(errno))); } sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1); if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == -1) { - throw std::runtime_error("Erreur connexion socket: " + std::string(strerror(errno))); + throw std::runtime_error("Error socket connexion: " + std::string(strerror(errno))); } } ~UnixSocketStrategy() { close(); } - void write(const std::string& data) override { - if (send(sock, data.c_str(), data.size(), 0) == -1) { - throw std::runtime_error("Erreur envoi socket: " + std::string(strerror(errno))); + void write(const std::string& data) override + { + if (sock == -1) + { + throw ctrace::ipc::SocketError(std::string("Socket is not connected: ") + strerror(errno)); + } + if (send(sock, data.c_str(), data.size(), 0) == -1) + { + throw ctrace::ipc::SocketError(std::string("Connection failed: ") + strerror(errno)); } } void close() override {