Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Basalaev.Daniil/laba1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build/
.vscode
13 changes: 13 additions & 0 deletions Basalaev.Daniil/laba1/Demon/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.5)

project(Demon)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(src)
add_subdirectory(test)

add_executable(Demon main.cpp)
target_link_libraries(Demon PRIVATE DemonLib)
3 changes: 3 additions & 0 deletions Basalaev.Daniil/laba1/Demon/config.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Demon/src
Demon/test
30
14 changes: 14 additions & 0 deletions Basalaev.Daniil/laba1/Demon/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "Demon.hpp"
#include <iostream>

int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cout << "Failed. No path to config was recieved" << std::endl;
return 1;
}
const char* configPath = argv[1];
Demon::getInstance().start(configPath);
return 0;
}
5 changes: 5 additions & 0 deletions Basalaev.Daniil/laba1/Demon/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
set(SRC_LIST Demon.cpp Logger.cpp Reader.cpp)

add_library(DemonLib STATIC ${SRC_LIST})

target_include_directories(DemonLib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
159 changes: 159 additions & 0 deletions Basalaev.Daniil/laba1/Demon/src/Demon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include "Demon.hpp"
#include "Logger.hpp"
#include "Reader.hpp"
#include <fstream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <syslog.h>
#include <fcntl.h>
#include <cstring>
#include <dirent.h>
#include <ctime>

static std::string const HIST_FILE{"/hist.log"};

Demon& Demon::getInstance()
{
static Demon instance;
return instance;
}

Demon::Demon() : m_reader(Reader::getInstance()), m_logger(Logger::getInstance()) {}

void Demon::start(const char* configPath)
{
m_logger.openLog("Demon");

if (isAlreadyRunning())
{
m_logger.logInfo("Demon is already running. Re-start");
}

if (!m_reader.readConfig(configPath))
{
m_logger.logError("Failed to open config. Destroy");
unlink(PID_FILE);
m_logger.closeLog();
exit(EXIT_FAILURE);
}

demonize();
handleSignals();

m_logger.logInfo("Demon start.");

run();
}

void Demon::demonize()
{
pid_t pid = fork();
if (pid < 0) exit(EXIT_FAILURE);
if (pid > 0) exit(EXIT_SUCCESS);

if (setsid() < 0) exit(EXIT_FAILURE);

signal(SIGHUP, SIG_IGN);
pid = fork();
if (pid < 0) exit(EXIT_FAILURE);
if (pid > 0) exit(EXIT_SUCCESS);

umask(0);
chdir("/");

close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

if (std::ofstream pidFile(PID_FILE); pidFile)
{
pidFile << getpid() << std::endl;
}
else
{
m_logger.logError("Failed to create PID file.");
exit(EXIT_FAILURE);
}
}

bool Demon::isAlreadyRunning() const
{
if (std::ifstream pidFile(PID_FILE); pidFile)
{
pid_t pid;
pidFile >> pid;
if (kill(pid, 0) == 0)
{
kill(pid, SIGTERM);
return true;
}
}
return false;
}

void Demon::handleSignals()
{
signal(SIGHUP, sighupHandler);
signal(SIGTERM, sigtermHandler);
}

void Demon::sighupHandler(int)
{
Logger::getInstance().logInfo("SIGHUP received, re-reading config.");
if (!Reader::getInstance().readConfig())
{
Logger::getInstance().logError("Failed to open config. Destroy");
unlink(PID_FILE);
Logger::getInstance().closeLog();
exit(EXIT_FAILURE);
}
}

void Demon::sigtermHandler(int)
{
Logger::getInstance().logInfo("SIGTERM received, terminating.");
unlink(PID_FILE);
Logger::getInstance().closeLog();
exit(0);
}

void Demon::run()
{
while (true)
{
monitor(m_reader.getDir1(), m_reader.getDir2() + HIST_FILE);
sleep(m_reader.getInterval());
}
}

void Demon::monitor(std::string const& dirPath, std::string const& logFile)
{
DIR* dir = opendir(dirPath.c_str());
if (!dir)
{
m_logger.logError("Failed to open directory: " + dirPath);
return;
}

std::ofstream log(logFile, std::ios_base::app);
if (!log)
{
m_logger.logError("Failed to open log file: " + logFile);
closedir(dir);
return;
}

time_t now = time(nullptr);
log << "Directory snapshot at: " << ctime(&now) << std::endl;

while (auto* entry = readdir(dir))
{
log << entry->d_name << std::endl;
}

log << std::endl;
m_logger.logInfo("Folder " + dirPath + " successfully scaned to " + logFile);
closedir(dir);
}
33 changes: 33 additions & 0 deletions Basalaev.Daniil/laba1/Demon/src/Demon.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <string>

inline const char* PID_FILE = "/var/run/Demon.pid";

class Reader;
class Logger;

class Demon
{
public:
static Demon& getInstance();
void start(const char* configPath);
void monitor(std::string const& dirPath, std::string const& logFile);
bool isAlreadyRunning() const;

private:
Demon();
Demon(Demon const&) = delete;
Demon& operator=(Demon const&) = delete;

void demonize();
void handleSignals();

void run();

static void sighupHandler(int signum);
static void sigtermHandler(int signum);

Reader& m_reader;
Logger& m_logger;
};
30 changes: 30 additions & 0 deletions Basalaev.Daniil/laba1/Demon/src/Logger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "Logger.hpp"
#include <syslog.h>

Logger& Logger::getInstance()
{
static Logger instance;
return instance;
}

Logger::Logger() {}

void Logger::openLog(std::string const& identifier) const
{
openlog(identifier.c_str(), LOG_PID, LOG_DAEMON);
}

void Logger::logInfo(std::string const& message) const
{
syslog(LOG_INFO, "%s", message.c_str());
}

void Logger::logError(std::string const& message) const
{
syslog(LOG_ERR, "%s", message.c_str());
}

void Logger::closeLog() const
{
closelog();
}
19 changes: 19 additions & 0 deletions Basalaev.Daniil/laba1/Demon/src/Logger.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <string>

class Logger
{
public:
static Logger& getInstance();

void openLog(std::string const& identifier) const;
void logInfo(std::string const& message) const;
void logError(std::string const& message) const;
void closeLog() const;

private:
Logger();
Logger(Logger const&) = delete;
Logger& operator=(Logger const&) = delete;
};
61 changes: 61 additions & 0 deletions Basalaev.Daniil/laba1/Demon/src/Reader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "Reader.hpp"
#include <fstream>
#include <filesystem>

std::string getAbsPath(std::string const& path)
{
char absPath[4096];
if (realpath(path.c_str(), absPath) == nullptr) { return ""; }
return absPath;
}

Reader& Reader::getInstance()
{
static Reader instance;
return instance;
}

bool Reader::readConfig(std::string const& configPath)
{
// set config path
if (m_configPath.empty())
{
m_configPath = getAbsPath(configPath);
if (m_configPath.empty()) { return false; }
}

// open config
std::ifstream configFile(m_configPath);
if (!configFile) { return false; }

static auto setDir = [](std::string& dir)
{
static auto currentDir = std::filesystem::current_path();

if (auto pathDir1 = std::filesystem::path(dir); pathDir1.is_absolute() && std::filesystem::is_directory(pathDir1))
{
dir = pathDir1.c_str();
}
else if (auto fullPathDir1 = currentDir / pathDir1; std::filesystem::is_directory(fullPathDir1))
{
dir = fullPathDir1.c_str();
}
else
{
return false;
}
return true;
};

// set dirs
std::getline(configFile, m_dir1);
if (!setDir(m_dir1)) { return false; }

std::getline(configFile, m_dir2);
if (!setDir(m_dir2)) { return false; }

configFile >> m_interval;
if (m_interval <= 0) { return false; }

return true;
}
26 changes: 26 additions & 0 deletions Basalaev.Daniil/laba1/Demon/src/Reader.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <string>

inline std::string const CONFIG_PATH{"Demon/config.txt"};

class Reader
{
public:
static Reader& getInstance();
bool readConfig(std::string const& configPath = CONFIG_PATH);

std::string const& getDir1() const noexcept { return m_dir1; }
std::string const& getDir2() const noexcept { return m_dir2; }
int getInterval() const noexcept { return m_interval; }

private:
Reader() = default;
Reader(const Reader&) = delete;
Reader& operator=(const Reader&) = delete;

std::string m_configPath;
std::string m_dir1;
std::string m_dir2;
int m_interval;
};
18 changes: 18 additions & 0 deletions Basalaev.Daniil/laba1/Demon/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
include(FetchContent)

FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.6.0
)

FetchContent_MakeAvailable(Catch2)

set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src)

set(SRC_LIST_TESTS ut.cpp)

add_executable(test ${SRC_LIST_TESTS})

target_include_directories(test PRIVATE ${SOURCE_DIR})
target_link_libraries(test PRIVATE Catch2::Catch2WithMain DemonLib)
Loading