Skip to content

Commit

Permalink
feat: Implement a monitor for the agent.
Browse files Browse the repository at this point in the history
  • Loading branch information
lchico committed Dec 23, 2024
1 parent 90ca827 commit 2c86342
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 49 deletions.
3 changes: 1 addition & 2 deletions src/agent/src/agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,7 @@ void Agent::Run()
},
m_messageQueue);
} else if (cmd.Module == "restart") {
requires_restart = true;
SignalHandler::HandleSignal(SIGTERM);
kill(getppid(), SIGUSR1);
auto RestartExecuteCommand = []() -> boost::asio::awaitable<module_command::CommandExecutionResult> {
co_return module_command::CommandExecutionResult{
module_command::Status::IN_PROGRESS,
Expand Down
172 changes: 126 additions & 46 deletions src/agent/src/process_options_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,125 @@
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <logger.hpp>
#include <unix_daemon.hpp>
#include <fstream>

#include <ctime>

#include <csignal>
#include <iostream>
#include <thread>
#include <vector>

#include <sys/wait.h>


// Flag to signal that SIGUSR1 was received
volatile sig_atomic_t signal_received = 0;

// Signal handler for SIGUSR1
void sigusr1_handler(int signal) {
if (signal == SIGUSR1) {
LogDebug("Received SIGUSR1: Restarting child agent process...");
signal_received = SIGUSR1; // Set flag to indicate restart needed
}
}

// Signal handler for SIGCHLD
void sigchld_handler(int signal) {
if (signal == SIGCHLD) {
LogDebug("Received SIGCHLD: Child agent process terminated." );
}
}


// Signal handler for SIGTERM
void sigterm_handler(int signal) {
if (signal == SIGTERM) {
LogDebug("Received SIGTERM: Stop the agent process...");
signal_received = SIGTERM; // Set flag to indicate restart needed
}
}

void StartAgent(const std::string& configFilePath)
{
bool needsRestart = false;
// Set up signal handlers
struct sigaction sa_usr1, sa_chld, sa_term;

sa_usr1.sa_handler = sigusr1_handler;
sa_usr1.sa_flags = 0;
sigaction(SIGUSR1, &sa_usr1, nullptr);

sa_chld.sa_handler = sigchld_handler;
sa_chld.sa_flags = SA_NOCLDSTOP; // Avoid receiving SIGCHLD for stopped children
sigaction(SIGCHLD, &sa_chld, nullptr);

// Set up SIGTERM handler
sa_term.sa_handler = sigterm_handler;
sa_term.sa_flags = 0;
sigaction(SIGTERM, &sa_term, nullptr);


unix_daemon::LockFileHandler lockFileHandler = unix_daemon::GenerateLockFile(configFilePath);

if (!lockFileHandler.isLockFileCreated())
{
std::cout << "wazuh-agent already running\n";
return;
}

LogInfo("Starting wazuh-agent");
pid_t pid = fork();

try
{
Agent agent(configFilePath);
agent.Run();
needsRestart = agent.IsRestartRequired();
}
catch (const std::exception& e)
{
LogError("Exception thrown in wazuh-agent: {}", e.what());
}
if (pid < 0) {
LogError("Restart: Fork failed");
exit(1);
} else if (pid == 0) {
// Child process: Run the agent
if (!lockFileHandler.isLockFileCreated())
{
std::cout << "wazuh-agent already running\n";
return;
}

LogInfo("Starting wazuh-agent");
try
{
Agent agent(configFilePath);
agent.Run();
}
catch (const std::exception& e)
{
LogError("Exception thrown in wazuh-agent: {}", e.what());
}

lockFileHandler.removeLockFile();

lockFileHandler.removeLockFile();
exit(0);
} else {
// Parent process - Monitoring Agent, take care of slefrestart
pause(); // Suspend parent until a signal is received
std::cout << "Wait to the child process ends." << std::endl;

if ( needsRestart ){
RestartAgent();
// Stop Agent
if ( signal_received == SIGTERM ){
std::cout << "Received SIGTERM, terminating child process..." << std::endl;
kill(pid, SIGTERM); // Send SIGTERM to the child process
waitpid(pid, nullptr, 0); // Wait for the child to terminate
}

// Self-restart agent
if (signal_received == SIGUSR1) {
if ( using_systemctl() ) {
LogDebug("Restart: systemctl restarting wazuh agent service.");
std::system("systemctl restart wazuh-agent");
} else {
StopAgent(pid, &lockFileHandler);

std::vector<const char*> args = get_command_line_args();
LogDebug("Restart: starting wazuh agent in a new process.");
if (execve(args[0], const_cast<char* const*>(args.data()), nullptr) == -1) {
LogError("Failed to spawn new Wazuh agent process.");
}
}
}
exit(0); // Exit the parent process
}
}


void StatusAgent(const std::string& configFilePath)
{
std::cout << fmt::format("wazuh-agent is {}\n", unix_daemon::GetDaemonStatus(configFilePath));
Expand Down Expand Up @@ -75,32 +154,33 @@ bool using_systemctl(){
nullptr != std::getenv("INVOCATION_ID"));
}

void RestartAgent(){

if ( using_systemctl() )
{
LogDebug("Restart: systemctl restarting wazuh agent service.");
std::system("systemctl restart wazuh-agent");
}else{
std::vector<const char*> args = get_command_line_args();
pid_t pid = fork();
void StopAgent(pid_t pid, unix_daemon::LockFileHandler *lockFileHandler){

if (pid == 0) {
// Child process
setpgid(0, 0);
std::this_thread::sleep_for(std::chrono::seconds(1));
int status;
pid_t result;

LogDebug("Restart: starting wazuh agent in a new process.");
if (execve(args[0], const_cast<char* const*>(args.data()), nullptr) == -1) {
LogError("Failed to spawn new Wazuh agent process.");
}
exit(1);
} else if (pid < 0) {
LogError("Restart: Fork failed");
exit(1);
} else {
// Parent process
exit(0);
const int timeout = 30; // Timeout duration (in seconds) for killing the agent child process
time_t start_time = time(nullptr); // Record the start time to track the timeout duration

// Initiate the process termination by sending SIGTERM
kill(pid, SIGTERM);

while(true){
result = waitpid(pid, &status, WNOHANG); // Non-blocking check for agent process status

if (result == pid) {
LogDebug("Agent process terminated.");
break;
}

if (difftime(time(nullptr), start_time) > timeout) {
LogError("Timeout reached! Forcing agent process termination.");
kill(pid, SIGKILL);
lockFileHandler->removeLockFile();
}

// Sleep for a short time before checking again
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
}
26 changes: 26 additions & 0 deletions src/agent/src/process_options_unix.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <unix_daemon.hpp>

/// @brief Retrieves command-line arguments for `execve()`.
///
/// Parses `/proc/self/cmdline` to extract arguments.
Expand All @@ -15,3 +17,27 @@ static bool using_systemctl();
///
/// After the agent stops, initiates a start but in a new process.
void RestartAgent();

/// @brief Stops the agent by terminating the child process.
///
/// This function sends a SIGTERM signal to the agent process to stop it. If the agent process
/// does not stop within a specified timeout period (30 seconds), it forces termination with a SIGKILL signal.
///
/// @param pid The process ID of the agent to stop.
/// @param lockFileHandler Pointer to the lock file handler used to remove the lock file if necessary.
void StopAgent(pid_t pid, unix_daemon::LockFileHandler* lockFileHandler);


/// @brief Handles SIGUSR1 signal
///
/// This function is called when the SIGUSR1 signal is received.
/// It handles the self-restart actions triggered by this signal.
void sigusr1_handler(int signal);

/// @brief Handles SIGCHLD signal
///
/// This function is invoked when a child process terminates.
/// It manages the termination of child processes by handling the SIGCHLD signal.
void sigchld_handler(int signal);

void sigterm_handler(int signal);
2 changes: 1 addition & 1 deletion src/agent/src/unix/unix_daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ namespace unix_daemon
const std::string filename = fmt::format("{}/wazuh-agent.lock", m_lockFilePath);

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-magic-numbers)
int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (fd == -1)
{
LogError("Unable to open lock file: {}. Error: {} ({})", filename.c_str(), errno, std::strerror(errno));
Expand Down

0 comments on commit 2c86342

Please sign in to comment.