Skip to content

Commit 604de06

Browse files
authored
Merge pull request #39 from wazuh/enhancement/24-design-and-development-of-the-concurrency-control-module-for-the-new-agent
Development of the concurrency control module for the new agent
2 parents 824bb1a + f832d54 commit 604de06

17 files changed

+427
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
build/
2+
13
*.so
24
*.so.*
35
*.o

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "src/vcpkg"]
2+
path = src/vcpkg
3+
url = https://github.com/microsoft/vcpkg

BUILD.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Build Instructions
2+
3+
1. **Clone the Repository**
4+
5+
First, clone the repository using the following command:
6+
7+
```bash
8+
git clone https://github.com/wazuh/wazuh-agent.git
9+
```
10+
11+
2. **Initialize Submodules**
12+
13+
The project uses submodules, so you need to initialize and update them. Run the following commands:
14+
15+
```bash
16+
cd wazuh-agent
17+
git submodule update --init --recursive
18+
```
19+
20+
3. **Build the Project**
21+
22+
Navigate to the `src` folder and create a `build` directory:
23+
24+
```bash
25+
cd src
26+
mkdir build
27+
cd build
28+
```
29+
30+
Run `cmake` to configure the project:
31+
32+
```bash
33+
cmake ..
34+
```
35+
36+
If you want to include tests, configure the project with the following command:
37+
38+
```bash
39+
cmake .. -DBUILD_TESTS=1
40+
```
41+
42+
4. **Build the Project**
43+
44+
Finally, build the project using `make`:
45+
46+
```bash
47+
make
48+
```
49+
50+
## Notes
51+
52+
- The project uses `vcpkg` as a submodule to manage dependencies. By initializing the submodules, `vcpkg` will automatically fetch the necessary dependencies when running CMake.
53+
- Ensure you have `cmake` and `make` installed on your system. You can install them via your package manager.

src/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cmake_minimum_required(VERSION 3.22)
2+
3+
set(CMAKE_CXX_STANDARD 20)
4+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
5+
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake")
6+
7+
project(Wazuh-Agent)
8+
9+
add_subdirectory(agent)

src/agent/CMakeLists.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
cmake_minimum_required(VERSION 3.22)
2+
3+
set(CMAKE_CXX_STANDARD 20)
4+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
5+
6+
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/../vcpkg/scripts/buildsystems/vcpkg.cmake")
7+
set(VCPKG_MANIFEST_DIR ${CMAKE_SOURCE_DIR}/../)
8+
9+
project(Agent)
10+
11+
include_directories(include)
12+
13+
set(SOURCES
14+
src/agent.cpp
15+
src/message_task.cpp
16+
src/task_manager.cpp
17+
)
18+
19+
set(HEADERS
20+
include/agent.hpp
21+
include/itask_manager.hpp
22+
include/task_manager.hpp
23+
include/message_task.hpp
24+
)
25+
26+
add_library(agent ${SOURCES} ${HEADERS})
27+
28+
find_package(Boost REQUIRED COMPONENTS asio)
29+
30+
target_link_libraries(agent PRIVATE ${Boost_LIBRARIES})
31+
32+
if(BUILD_TESTS)
33+
add_subdirectory(tests)
34+
endif()

src/agent/include/agent.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#include <task_manager.hpp>
4+
5+
#include <queue>
6+
#include <string>
7+
8+
class Agent
9+
{
10+
public:
11+
Agent();
12+
~Agent();
13+
14+
private:
15+
std::queue<std::string> m_messageQueue;
16+
17+
TaskManager m_taskManager;
18+
};

src/agent/include/itask_manager.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
3+
#include <functional>
4+
5+
template<typename CoroutineTaskType>
6+
class ITaskManager
7+
{
8+
public:
9+
virtual ~ITaskManager() = default;
10+
11+
virtual void start(size_t numThreads) = 0;
12+
virtual void stop() = 0;
13+
14+
virtual void enqueueTask(std::function<void()> task) = 0;
15+
virtual void enqueueTask(CoroutineTaskType task) = 0;
16+
};

src/agent/include/message_task.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
3+
#include <boost/asio.hpp>
4+
#include <boost/beast.hpp>
5+
6+
#include <queue>
7+
#include <string>
8+
9+
boost::asio::awaitable<void> StatefulMessageProcessingTask(std::queue<std::string>& messageQueue);
10+
11+
boost::asio::awaitable<void> StatelessMessageProcessingTask(std::queue<std::string>& messageQueue);

src/agent/include/task_manager.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
#include <itask_manager.hpp>
4+
5+
#include <boost/asio/awaitable.hpp>
6+
#include <boost/asio/io_context.hpp>
7+
8+
#include <functional>
9+
#include <thread>
10+
#include <vector>
11+
12+
class TaskManager : public ITaskManager<boost::asio::awaitable<void>>
13+
{
14+
public:
15+
TaskManager();
16+
17+
void start(size_t numThreads) override;
18+
void stop() override;
19+
20+
void enqueueTask(std::function<void()> task) override;
21+
void enqueueTask(boost::asio::awaitable<void> task) override;
22+
23+
private:
24+
boost::asio::io_context m_ioContext;
25+
boost::asio::io_context::work m_work;
26+
std::vector<std::thread> m_threads;
27+
};

src/agent/src/agent.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include "agent.hpp"
2+
3+
#include <message_task.hpp>
4+
5+
#include <chrono>
6+
#include <thread>
7+
8+
Agent::Agent()
9+
{
10+
m_taskManager.start(std::thread::hardware_concurrency());
11+
m_taskManager.enqueueTask(StatefulMessageProcessingTask(m_messageQueue));
12+
m_taskManager.enqueueTask(StatelessMessageProcessingTask(m_messageQueue));
13+
}
14+
15+
Agent::~Agent()
16+
{
17+
// Sleep for 2 seconds
18+
std::this_thread::sleep_for(std::chrono::seconds(2));
19+
m_taskManager.stop();
20+
}

src/agent/src/message_task.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include <message_task.hpp>
2+
3+
#include <chrono>
4+
#include <iostream>
5+
6+
namespace
7+
{
8+
9+
boost::asio::awaitable<void> send_http_request(const std::string host,
10+
const std::string port,
11+
const std::string target,
12+
std::queue<std::string>& messageQueue)
13+
{
14+
using namespace std::chrono_literals;
15+
16+
auto executor = co_await boost::asio::this_coro::executor;
17+
boost::asio::steady_timer timer(executor);
18+
boost::asio::ip::tcp::resolver resolver(executor);
19+
20+
while (true)
21+
{
22+
boost::beast::error_code ec;
23+
boost::asio::ip::tcp::socket socket(executor);
24+
25+
auto const results = co_await resolver.async_resolve(host, port, boost::asio::use_awaitable);
26+
co_await boost::asio::async_connect(socket, results, boost::asio::use_awaitable);
27+
28+
while (true)
29+
{
30+
std::string message;
31+
{
32+
if (messageQueue.empty())
33+
{
34+
timer.expires_after(100ms);
35+
co_await timer.async_wait(boost::asio::use_awaitable);
36+
continue;
37+
}
38+
message = std::move(messageQueue.front());
39+
messageQueue.pop();
40+
}
41+
42+
// HTTP request
43+
boost::beast::http::request<boost::beast::http::string_body> req {
44+
boost::beast::http::verb::post, target, 11};
45+
req.set(boost::beast::http::field::host, host);
46+
req.set(boost::beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING);
47+
req.body() = message;
48+
req.prepare_payload();
49+
co_await boost::beast::http::async_write(
50+
socket, req, boost::asio::redirect_error(boost::asio::use_awaitable, ec));
51+
52+
if (ec)
53+
{
54+
socket.close();
55+
break;
56+
}
57+
58+
// HTTP response
59+
boost::beast::flat_buffer buffer;
60+
boost::beast::http::response<boost::beast::http::dynamic_body> res;
61+
co_await boost::beast::http::async_read(
62+
socket, buffer, res, boost::asio::redirect_error(boost::asio::use_awaitable, ec));
63+
64+
if (ec)
65+
{
66+
socket.close();
67+
break;
68+
}
69+
}
70+
}
71+
}
72+
73+
} // namespace
74+
75+
boost::asio::awaitable<void> StatefulMessageProcessingTask(std::queue<std::string>& messageQueue)
76+
{
77+
co_await send_http_request("localhost", "8080", "/stateless", messageQueue);
78+
}
79+
80+
boost::asio::awaitable<void> StatelessMessageProcessingTask(std::queue<std::string>& messageQueue)
81+
{
82+
co_await send_http_request("localhost", "8080", "/stateful", messageQueue);
83+
}

src/agent/src/task_manager.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <task_manager.hpp>
2+
3+
#include <boost/asio/co_spawn.hpp>
4+
#include <boost/asio/detached.hpp>
5+
#include <boost/asio/post.hpp>
6+
7+
TaskManager::TaskManager()
8+
: m_work(m_ioContext)
9+
{
10+
}
11+
12+
void TaskManager::start(size_t numThreads)
13+
{
14+
stop();
15+
16+
for (size_t i = 0; i < numThreads; ++i)
17+
{
18+
m_threads.emplace_back([this]() { m_ioContext.run(); });
19+
}
20+
}
21+
22+
void TaskManager::stop()
23+
{
24+
m_ioContext.stop();
25+
26+
for (std::thread& thread : m_threads)
27+
{
28+
if (thread.joinable())
29+
{
30+
thread.join();
31+
}
32+
}
33+
m_threads.clear();
34+
m_ioContext.reset();
35+
}
36+
37+
void TaskManager::enqueueTask(std::function<void()> task)
38+
{
39+
boost::asio::post(m_ioContext, std::move(task));
40+
}
41+
42+
void TaskManager::enqueueTask(boost::asio::awaitable<void> task)
43+
{
44+
boost::asio::co_spawn(m_ioContext, std::move(task), boost::asio::detached);
45+
}

src/agent/tests/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Wazuh Agent tests
2+
3+
find_package(GTest CONFIG REQUIRED)
4+
5+
set(TEST_SOURCES
6+
agent_test.cpp
7+
task_manager_test.cpp
8+
)
9+
10+
add_executable(agent_test agent_test.cpp)
11+
target_link_libraries(agent_test PRIVATE agent GTest::gtest)
12+
add_test(NAME AgentTest COMMAND agent_test)
13+
14+
add_executable(task_manager_test task_manager_test.cpp)
15+
target_link_libraries(task_manager_test PRIVATE agent GTest::gtest)
16+
add_test(NAME TaskManagerTest COMMAND task_manager_test)

src/agent/tests/agent_test.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <agent.hpp>
2+
3+
#include <gtest/gtest.h>
4+
5+
TEST(AgentTests, AgentConstruction)
6+
{
7+
EXPECT_NO_THROW(Agent {});
8+
}
9+
10+
int main(int argc, char** argv)
11+
{
12+
::testing::InitGoogleTest(&argc, argv);
13+
return RUN_ALL_TESTS();
14+
}

0 commit comments

Comments
 (0)