diff --git a/AsanovDamir/laba1/.gitignore b/AsanovDamir/laba1/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/AsanovDamir/laba1/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/AsanovDamir/laba1/CMakeLists.txt b/AsanovDamir/laba1/CMakeLists.txt new file mode 100644 index 0000000..f6dcab7 --- /dev/null +++ b/AsanovDamir/laba1/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.5) + +project(DamirAsanov) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") + +add_executable(DamirAsanov main.cpp Deamon.cpp Config.cpp) diff --git a/AsanovDamir/laba1/Config.cpp b/AsanovDamir/laba1/Config.cpp new file mode 100644 index 0000000..3eb1621 --- /dev/null +++ b/AsanovDamir/laba1/Config.cpp @@ -0,0 +1,37 @@ +#include "Config.hpp" +#include +#include + +std::string Config::get_absolute_path(const char* pathStr) +{ + std::filesystem::path absPath = std::filesystem::absolute(pathStr); + if (std::filesystem::exists(absPath)) + return absPath.c_str(); + else + return ""; + + return std::filesystem::absolute(pathStr).c_str(); +} + +Config::Config() : pathToConfig{get_absolute_path("Config.txt")} +{} + +bool Config::read_config() +{ + std::ifstream config(pathToConfig.c_str()); + if (!config) return false; + + std::getline(config, folder1); + folder1 = get_absolute_path(folder1.c_str()); + std::getline(config, folder2); + folder2 = get_absolute_path(folder2.c_str()); + config >> seconds; + + if (folder1.empty() || folder2.empty() || seconds == 0 || pathToConfig.empty()) return false; + return true; +} + +void Config::set_path_to_config(const char* path_to_config) +{ + pathToConfig = get_absolute_path(path_to_config); +} diff --git a/AsanovDamir/laba1/Config.hpp b/AsanovDamir/laba1/Config.hpp new file mode 100644 index 0000000..d2c1d56 --- /dev/null +++ b/AsanovDamir/laba1/Config.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +class Config +{ +public: + Config(); + bool read_config(); + + const std::string& get_folder1() const { return folder1; } + const std::string& get_folder2() const { return folder2; } + int get_seconds() const { return seconds; } + void set_path_to_config(const char* path_to_config); + +private: + std::string get_absolute_path(const char* path); + + std::string pathToConfig; + std::string folder1; + std::string folder2; + int seconds; +}; diff --git a/AsanovDamir/laba1/Config.txt b/AsanovDamir/laba1/Config.txt new file mode 100644 index 0000000..ba258b0 --- /dev/null +++ b/AsanovDamir/laba1/Config.txt @@ -0,0 +1,3 @@ +FirstFolder +SecondFolder +10 \ No newline at end of file diff --git a/AsanovDamir/laba1/Deamon.cpp b/AsanovDamir/laba1/Deamon.cpp new file mode 100644 index 0000000..78b4a0c --- /dev/null +++ b/AsanovDamir/laba1/Deamon.cpp @@ -0,0 +1,167 @@ +#include "Deamon.hpp" +#include +#include +#include +#include + +Config Deamon::config; + +const char* PID = "/var/run/Deamon.pid"; + +Deamon& Deamon::get_instance() +{ + static Deamon instance; + return instance; +} + +void Deamon::destroy_prev_deamon_if_exist() +{ + if (std::ifstream pid_file(PID); pid_file) { + pid_t pid; + pid_file >> pid; + if (kill(pid, 0) == 0) { + Logger::get_instance().log(Status::INFO, "Deamon is already running. Killing the old instance..."); + if (kill(pid, SIGTERM) != 0) { + Logger::get_instance().log(Status::ERROR, "Failed to terminate the existing daemon process."); + unlink(PID); + } + } + } +} + +bool Deamon::crate_pid_file() +{ + std::ofstream new_pid_file(PID); + if (!new_pid_file) { + Logger::get_instance().log(Status::ERROR, "Failed to create PID file."); + return false; + } + + new_pid_file << getpid(); + return true; +} + +void Deamon::daemonize() +{ + 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); +} + +bool Deamon::coping() +{ + namespace fs = std::filesystem; + + const std::string& folder1 = config.get_folder1(); + const std::string& folder2 = config.get_folder2(); + + const std::string imgDir = folder2 + "/IMG"; + const std::string othersDir = folder2 + "/OTHERS"; + + try { + // Clear folder 2 + if (fs::exists(folder2)) { + for (const auto& entry : fs::directory_iterator(folder2)) { + fs::remove_all(entry.path()); + } + } else { + fs::create_directories(folder2); + } + + // Craate IMG and OTHERS folders + fs::create_directories(imgDir); + fs::create_directories(othersDir); + + // coping + for (const auto& entry : fs::directory_iterator(folder1)) { + if (entry.is_regular_file()) { + std::string fileName = entry.path().filename().string(); + std::string destPath; + if (entry.path().extension() == ".png") { + destPath = imgDir + "/" + fileName; + } else { + destPath = othersDir + "/" + fileName; + } + fs::copy(entry.path(), destPath); + } + } + } catch (const std::exception& ex) { + Logger::get_instance().log(Status::ERROR, "Error during copying: " + std::string(ex.what())); + return false; + } + + Logger::get_instance().log(Status::INFO, "Copied files from" + folder1 + " to " + folder2); + return true; +} + +void Deamon::start(const char* config_path) +{ + config.set_path_to_config(config_path); + + destroy_prev_deamon_if_exist(); + daemonize(); + + Logger::get_instance().open_log(); + Logger::get_instance().log(Status::INFO, "Start deamon"); + + if (!config.read_config()) { + + Logger::get_instance().log(Status::ERROR, std::string("Failed to read config")); + exit(EXIT_FAILURE); + } + + if (!crate_pid_file()) + exit(EXIT_FAILURE); + + handle_signals(); + + while (true) { + coping(); + sleep(config.get_seconds()); + } +} + +void Deamon::handle_signals() +{ + signal(SIGHUP, sighup_handler); + signal(SIGTERM, sigterm_handler); +} + +void Deamon::sighup_handler(int) +{ + Logger::get_instance().log(Status::INFO, "Re-reading config"); + if (!config.read_config()) { + Logger::get_instance().log(Status::ERROR, "Failed to read config"); + Logger::get_instance().close_log(); + unlink(PID); + exit(EXIT_FAILURE); + } +} + +void Deamon::sigterm_handler(int) +{ + Logger::get_instance().log(Status::INFO, "Terminating"); + Logger::get_instance().close_log(); + unlink(PID); + exit(0); +} diff --git a/AsanovDamir/laba1/Deamon.hpp b/AsanovDamir/laba1/Deamon.hpp new file mode 100644 index 0000000..e4496eb --- /dev/null +++ b/AsanovDamir/laba1/Deamon.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "Logger.hpp" +#include "Config.hpp" + +class Deamon +{ +public: + static Deamon& get_instance(); + void start(const char* config_path); + bool coping(); + +private: + Deamon() = default; + Deamon(const Deamon&) = delete; + Deamon& operator=(const Deamon&) = delete; + + void handle_signals(); + static void sighup_handler(int signum); + static void sigterm_handler(int signum); + + void daemonize(); + bool crate_pid_file(); + void destroy_prev_deamon_if_exist(); + + static Config config; +}; \ No newline at end of file diff --git a/AsanovDamir/laba1/FirstFolder/some.png b/AsanovDamir/laba1/FirstFolder/some.png new file mode 100644 index 0000000..e69de29 diff --git a/AsanovDamir/laba1/FirstFolder/some.txt b/AsanovDamir/laba1/FirstFolder/some.txt new file mode 100644 index 0000000..e69de29 diff --git a/AsanovDamir/laba1/Logger.hpp b/AsanovDamir/laba1/Logger.hpp new file mode 100644 index 0000000..53e9d7c --- /dev/null +++ b/AsanovDamir/laba1/Logger.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +enum class Status : short +{ +INFO = LOG_INFO, +ERROR = LOG_ERR +}; + +class Logger +{ +public: + static Logger& get_instance() + { + static Logger instance; + return instance; + } + + void open_log() + { + openlog("Deamon", LOG_PID, LOG_DAEMON); + } + void log(Status status, const std::string& message) + { + syslog(static_cast(status), "%s", message.c_str()); + } + void close_log() { closelog(); } + +private: + Logger() = default; + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; +}; diff --git a/AsanovDamir/laba1/README.md b/AsanovDamir/laba1/README.md new file mode 100644 index 0000000..ceb5ae7 --- /dev/null +++ b/AsanovDamir/laba1/README.md @@ -0,0 +1,22 @@ +# Проект: Demon + +# Автор + +Асанов Дамир Русланович 5030102/10201 Лабораторная работа №1 вариант №9: + +## Описание + +Копировать содержимое папки 1 в папку 2(предварительно очищая содержимое папки 2) следующим образом: файлы с расширением “*.png” +должны помещаться в подпапку “IMG” папки 2, остальные — в подпапку “OTHERS” + +## Запуск + +введите команду ```bash build.sh```, чтобы собрать проект +введите команду ```bash run.sh```, чтобы запустить демона +введите команду ```bash kill```, чтобы остановить демона +введите команду ```bash reconfig```, чтобы демон перепрочитал конфиг + +форма конфига: +первая строчка - путь к первой папке, откуда будет происоходить копирование +вторая строчка - путь ко второй папке, куда будет копироваться +третья строчка - целое число - количество секунд между операцией копирования diff --git a/AsanovDamir/laba1/SecondFolder/IMG/some.png b/AsanovDamir/laba1/SecondFolder/IMG/some.png new file mode 100644 index 0000000..e69de29 diff --git a/AsanovDamir/laba1/SecondFolder/OTHERS/some.txt b/AsanovDamir/laba1/SecondFolder/OTHERS/some.txt new file mode 100644 index 0000000..e69de29 diff --git a/AsanovDamir/laba1/build.sh b/AsanovDamir/laba1/build.sh new file mode 100644 index 0000000..dabf60f --- /dev/null +++ b/AsanovDamir/laba1/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +mkdir -p build +cd build +cmake .. +make + +find . -type f -name '*.o' -delete +find . -type f -name '*.cmake' -delete +find . -type f -name 'CMakeCache.txt' -delete +find . -type f -name 'Makefile' -delete +rm -rf CMakeFiles diff --git a/AsanovDamir/laba1/kill b/AsanovDamir/laba1/kill new file mode 100644 index 0000000..e479856 --- /dev/null +++ b/AsanovDamir/laba1/kill @@ -0,0 +1,2 @@ +#!/bin/bash +sudo kill -SIGTERM $(cat /var/run/Deamon.pid) diff --git a/AsanovDamir/laba1/main.cpp b/AsanovDamir/laba1/main.cpp new file mode 100644 index 0000000..17d41c2 --- /dev/null +++ b/AsanovDamir/laba1/main.cpp @@ -0,0 +1,14 @@ +#include "Deamon.hpp" +#include + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + std::cout << "Wrong count of argument. Expected path to config" << std::endl; + return 1; + } + const char* config_path = argv[1]; + Deamon::get_instance().start(config_path); + return 0; +} \ No newline at end of file diff --git a/AsanovDamir/laba1/reconfig b/AsanovDamir/laba1/reconfig new file mode 100644 index 0000000..f2bf687 --- /dev/null +++ b/AsanovDamir/laba1/reconfig @@ -0,0 +1,2 @@ +#!/bin/bash +sudo kill -SIGHUP $(cat /var/run/Deamon.pid) diff --git a/AsanovDamir/laba1/run.sh b/AsanovDamir/laba1/run.sh new file mode 100644 index 0000000..e33b684 --- /dev/null +++ b/AsanovDamir/laba1/run.sh @@ -0,0 +1 @@ +sudo ./build/DamirAsanov Config.txt \ No newline at end of file diff --git a/AsanovDamir/laba2/.gitignore b/AsanovDamir/laba2/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/AsanovDamir/laba2/.gitignore @@ -0,0 +1 @@ +build diff --git a/AsanovDamir/laba2/Book.hpp b/AsanovDamir/laba2/Book.hpp new file mode 100644 index 0000000..824b13c --- /dev/null +++ b/AsanovDamir/laba2/Book.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +struct Book +{ + std::string name; + int count; +}; diff --git a/AsanovDamir/laba2/CMakeLists.txt b/AsanovDamir/laba2/CMakeLists.txt new file mode 100644 index 0000000..75f3fce --- /dev/null +++ b/AsanovDamir/laba2/CMakeLists.txt @@ -0,0 +1,73 @@ +cmake_minimum_required(VERSION 3.5) + +project(DamirAsanov) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") + +find_package(Qt5 COMPONENTS Widgets REQUIRED) + +set(CMAKE_AUTOMOC ON) # for Q_OBJECT + +############ sockets ############ +set(SOURCES_SOCK + host_sock.cpp + HostUtils/HostWindow.cpp + ClientUtils/ClientWindow.cpp + conn_sock.cpp + semaphore.cpp +) + +set(HEADERS_SOCK + HostUtils/HostWindow.hpp + ClientUtils/ClientWindow.hpp + conn_sock.hpp + semaphore.hpp + logger.hpp +) + +add_executable(host_sock ${SOURCES_SOCK} ${HEADERS_SOCK}) +target_link_libraries(host_sock Qt5::Widgets) +target_include_directories(host_sock PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +############ queue ############ +set(SOURCES_MQ + host_mq.cpp + HostUtils/HostWindow.cpp + ClientUtils/ClientWindow.cpp + conn_mq.cpp + semaphore.cpp +) + +set(HEADERS_MQ + HostUtils/HostWindow.hpp + ClientUtils/ClientWindow.hpp + conn_mq.hpp + semaphore.hpp + logger.hpp +) + +add_executable(host_mq ${SOURCES_MQ} ${HEADERS_MQ}) +target_link_libraries(host_mq Qt5::Widgets) +target_include_directories(host_mq PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +############ fifo ############ +set(SOURCES_FIFO + host_fifo.cpp + HostUtils/HostWindow.cpp + ClientUtils/ClientWindow.cpp + conn_fifo.cpp + semaphore.cpp +) + +set(HEADERS_FIFO + HostUtils/HostWindow.hpp + ClientUtils/ClientWindow.hpp + conn_fifo.hpp + semaphore.hpp + logger.hpp +) + +add_executable(host_fifo ${SOURCES_FIFO} ${HEADERS_FIFO}) +target_link_libraries(host_fifo Qt5::Widgets) +target_include_directories(host_fifo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + diff --git a/AsanovDamir/laba2/ClientUtils/ClientWindow.cpp b/AsanovDamir/laba2/ClientUtils/ClientWindow.cpp new file mode 100644 index 0000000..e1df01e --- /dev/null +++ b/AsanovDamir/laba2/ClientUtils/ClientWindow.cpp @@ -0,0 +1,119 @@ +#include "ClientWindow.hpp" +#include +#include +#include +#include +#include "logger.hpp" + +ClientWindow::ClientWindow(const std::vector& books, QWidget* parent) + : QMainWindow(parent) { + stackedWidget = new QStackedWidget(this); + + createBookView(books); + createReadingView(); + createHistoryView(); + + setCentralWidget(stackedWidget); + setWindowTitle("Client Window"); + resize(400, 300); + + // Set started window + stackedWidget->setCurrentIndex(0); +} + +ClientWindow::~ClientWindow() {} + +void ClientWindow::createBookView(const std::vector& books) { + QWidget* bookView = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(bookView); + + bookList = new QListWidget(this); + for (const auto& book : books) { + bookList->addItem(QString::fromStdString(book.name) + " - " + QString::number(book.count) + " copies"); + } + layout->addWidget(bookList); + + selectButton = new QPushButton("Select Book", this); + selectButton->setEnabled(false); + layout->addWidget(selectButton); + + //terminateClientButton = new QPushButton("Terminate Client", this); + //layout->addWidget(terminateClientButton); + + // Enable button if book is choosed + connect(bookList, &QListWidget::itemSelectionChanged, [this]() { + selectButton->setEnabled(bookList->currentItem() != nullptr); + }); + connect(selectButton, &QPushButton::clicked, this, &ClientWindow::selectBook); + //connect(terminateClientButton, &QPushButton::clicked, this, &ClientWindow::terminateClient); + + stackedWidget->addWidget(bookView); +} + +void ClientWindow::createReadingView() { + QWidget* readingView = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(readingView); + + readingLabel = new QLabel("Reading book: ", this); + layout->addWidget(readingLabel); + + cancelReadingButton = new QPushButton("Cancel Reading", this); + layout->addWidget(cancelReadingButton); + + connect(cancelReadingButton, &QPushButton::clicked, this, &ClientWindow::cancelReading); + + stackedWidget->addWidget(readingView); +} + +void ClientWindow::createHistoryView() { + historyList = new QListWidget(this); + historyList->setFixedWidth(350); + auto* widget = new QDockWidget("History", this); + widget->setWidget(historyList); + addDockWidget(Qt::LeftDockWidgetArea, widget); +} + +void ClientWindow::selectBook() { + if (bookList->currentItem()) { + QString bookName = bookList->currentItem()->text().split(" - ").first(); + readingLabel->setText("Reading book: " + bookName); + + emit bookSelected(bookName); + } +} + +void ClientWindow::cancelReading() { + QString bookName = readingLabel->text().split(": ").last(); + emit bookReturned(bookName); + + // Set started window + stackedWidget->setCurrentIndex(0); + + addHistory("Cancelled reading: ", bookName, true); +} + +void ClientWindow::addHistory(const QString& action, const QString& bookName, bool success) { + QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"); + QString status = success ? "SUCCESS" : "FAIL"; + historyList->addItem(QString("[%1] %2 \"%3\": %4").arg(timestamp, action, bookName, status)); +} + +void ClientWindow::onSuccessTakeBook() { + // Set reading window + stackedWidget->setCurrentIndex(1); + + addHistory("TAKE book: ", bookList->currentItem()->text().split(" - ").first(), true); +} + +void ClientWindow::onFailedTakeBook() { + // Cannot display this window.... + // QMessageBox::warning(nullptr, "FAIL", "Failed to take book"); + LoggerClient::get_instance().log(Status::ERROR, "Failed to take book"); + + addHistory("TAKE book: ", bookList->currentItem()->text().split(" - ").first(), false); +} + +void ClientWindow::terminateClient() { + QMessageBox::information(this, "Terminate Client", "Client terminated."); + std::exit(0); // TODO: exit only qapp, but don't whole process +} diff --git a/AsanovDamir/laba2/ClientUtils/ClientWindow.hpp b/AsanovDamir/laba2/ClientUtils/ClientWindow.hpp new file mode 100644 index 0000000..5ade49b --- /dev/null +++ b/AsanovDamir/laba2/ClientUtils/ClientWindow.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Book.hpp" + +class ClientWindow : public QMainWindow { + Q_OBJECT + +public: + ClientWindow(const std::vector& books, QWidget* parent = nullptr); + ~ClientWindow(); + + void onSuccessTakeBook(); + void onFailedTakeBook(); + void addHistory(const QString& action, const QString& bookName, bool success); + +signals: + void bookSelected(const QString& bookName); + void bookReturned(const QString& bookName); + +private slots: + void selectBook(); + void cancelReading(); + void terminateClient(); + +private: + void createBookView(const std::vector& books); + void createReadingView(); + void createHistoryView(); + + QStackedWidget* stackedWidget; // For switch windows (started <-> reading) + + QListWidget* historyList; + QListWidget* bookList; + QPushButton* selectButton; + + QLabel* readingLabel; + QPushButton* cancelReadingButton; + + // QPushButton* terminateClientButton; +}; diff --git a/AsanovDamir/laba2/ClientUtils/clientProcessing.hpp b/AsanovDamir/laba2/ClientUtils/clientProcessing.hpp new file mode 100644 index 0000000..a199881 --- /dev/null +++ b/AsanovDamir/laba2/ClientUtils/clientProcessing.hpp @@ -0,0 +1,81 @@ +#include +#include "ClientWindow.hpp" +#include "conn_sock.hpp" +#include "logger.hpp" +#include "semaphore.hpp" +#include +#include +#include + +void listenForHostMessages(conn& conn, Semaphore& semaphore, ClientWindow& window, std::atomic& is_ranning) { + auto& logger = LoggerClient::get_instance(); + + while (is_ranning) { + + semaphore.Wait(); // Start critical section + char buffer[64] = {0}; + if (conn.Read(buffer, sizeof(buffer))) { + std::string response(buffer); + + if (response == "YES") { + window.onSuccessTakeBook(); + logger.log(Status::INFO, "Host gives the book"); + } + else if (response == "NO") { + window.onFailedTakeBook(); + logger.log(Status::INFO, "Host rejected the request: no such book"); + } + else { + logger.log(Status::ERROR, "Unexpected response from host: " + response); + } + } + + semaphore.Post(); // End critical section + sleep(0.01); + } +} + +int processClient(Semaphore& semaphore, conn& conn, const std::vector& books) { + auto& logger = LoggerClient::get_instance(); + + int argc = 0; + char** argv = nullptr; + QApplication app(argc, argv); + ClientWindow window(books); + + std::atomic is_running(true); + + // Start thread with listenning socket + std::thread listenerThread(listenForHostMessages, std::ref(conn), std::ref(semaphore), std::ref(window), std::ref(is_running)); + + QObject::connect(&window, &ClientWindow::bookSelected, [&semaphore, &conn, &logger](const QString& bookName) { + std::string request = "TAKE " + bookName.toStdString(); + if (conn.Write(request.c_str(), request.size())) { + logger.log(Status::INFO, "Requested book: " + bookName.toStdString()); + } + else { + logger.log(Status::ERROR, "Failed to request the book: " + bookName.toStdString()); + } + }); + + QObject::connect(&window, &ClientWindow::bookReturned, [&conn, &logger](const QString& bookName) { + std::string request = "RETURN " + bookName.toStdString(); + if (conn.Write(request.c_str(), request.size())) { + logger.log(Status::INFO, "Returned book: " + bookName.toStdString()); + } + else { + logger.log(Status::ERROR, "Failed to return the book: " + bookName.toStdString()); + } + }); + + window.show(); + int result = app.exec(); + + // Complete thread with listenning socket + is_running = false; + if (listenerThread.joinable()) { + listenerThread.join(); + } + + return result; +} \ No newline at end of file diff --git a/AsanovDamir/laba2/HostUtils/HostWindow.cpp b/AsanovDamir/laba2/HostUtils/HostWindow.cpp new file mode 100644 index 0000000..a682d2b --- /dev/null +++ b/AsanovDamir/laba2/HostUtils/HostWindow.cpp @@ -0,0 +1,110 @@ +#include "HostWindow.hpp" +#include "logger.hpp" +#include +#include +#include +#include +#include +#include + +static int secondsLeft = 5; + +HostWindow::HostWindow(const std::string& hostTitle, const std::vector& books, QWidget* parent) + : QMainWindow(parent) { + + QWidget* centralWidget = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(centralWidget); + + portLabel = new QLabel(QString::fromStdString(hostTitle), this); + layout->addWidget(portLabel); + + bookList = new QListWidget(this); + for (const auto& book : books) { + bookList->addItem(QString::fromStdString(book.name) + " - " + QString::number(book.count) + " copies"); + } + layout->addWidget(bookList); + + terminateClientButton = new QPushButton("Terminate Client", this); + layout->addWidget(terminateClientButton); + + terminateHostButton = new QPushButton("Terminate Host", this); + layout->addWidget(terminateHostButton); + + timerLabel = new QLabel("Time left: 5 seconds", this); + layout->addWidget(timerLabel); + + clientTimer = new QTimer(this); + clientTimer->setInterval(1000); + connect(clientTimer, &QTimer::timeout, this, [this]() { + if (!clientTimer->isActive()) return; + + if (--secondsLeft <= 0) { + terminateClient(); + } else { + timerLabel->setText(QString("Time left: %1 seconds").arg(secondsLeft)); + } + }); + + connect(terminateClientButton, &QPushButton::clicked, this, &HostWindow::terminateClient); + connect(terminateHostButton, &QPushButton::clicked, this, &HostWindow::terminateHost); + connect(this, &HostWindow::resetSignalTimer, this, &HostWindow::resetTimer); + connect(this, &HostWindow::stopSignalTimer, this, [this]() { + timerLabel->setText("Client reading"); + clientTimer->stop(); + }); + + historyList = new QListWidget(this); + QDockWidget* historyDock = new QDockWidget("History", this); + historyDock->setWidget(historyList); + addDockWidget(Qt::RightDockWidgetArea, historyDock); + + setCentralWidget(centralWidget); + setWindowTitle("Host Window"); + resize(600, 450); + + clientTimer->start(); +} + +HostWindow::~HostWindow() {} + +void HostWindow::updateBooks(const std::vector& books) { + bookList->clear(); + for (const auto& book : books) { + bookList->addItem(QString::fromStdString(book.name) + " - " + QString::number(book.count) + " copies"); + } +} + +void HostWindow::addHistory(const QString& action, const QString& bookName, bool success) { + QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"); + QString status = success ? "SUCCESS" : "FAIL"; + historyList->addItem(QString("[%1] Client ID: 1, %2 \"%3\": %4").arg(timestamp, action, bookName, status)); +} + +void HostWindow::terminateClient() { + clientTimer->stop(); + timerLabel->setText("Client terminated"); + QMessageBox::information(this, "Terminate Client", "Client terminated."); + LoggerHost::get_instance().log(Status::INFO, "Terminate Client"); + kill(clientPid, SIGKILL); +} + +void HostWindow::terminateHost() { + QMessageBox::information(this, "Terminate Host", "Host terminated."); + LoggerHost::get_instance().log(Status::INFO, "Terminate Host"); + kill(clientPid, SIGKILL); // and kill client too + std::exit(0); +} + +void HostWindow::resetTimer() { + secondsLeft = 5; + timerLabel->setText("Time left: 5 seconds"); + clientTimer->start(); +} + +void HostWindow::signalResetTimer() { + emit resetSignalTimer(); +} + +void HostWindow::signalStopTimer() { + emit stopSignalTimer(); +} diff --git a/AsanovDamir/laba2/HostUtils/HostWindow.hpp b/AsanovDamir/laba2/HostUtils/HostWindow.hpp new file mode 100644 index 0000000..fdf0083 --- /dev/null +++ b/AsanovDamir/laba2/HostUtils/HostWindow.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Book.hpp" + +class HostWindow : public QMainWindow { + Q_OBJECT + +public: + HostWindow(const std::string& hostTitle, const std::vector& books, QWidget* parent = nullptr); + virtual ~HostWindow(); + + void updateBooks(const std::vector& books); + void signalResetTimer(); + void signalStopTimer(); + + void addHistory(const QString& action, const QString& bookName, bool success); + + pid_t clientPid; // for kill + +signals: + void resetSignalTimer(); + void stopSignalTimer(); + +private slots: + void terminateClient(); + void terminateHost(); + void resetTimer(); + +private: + QLabel* portLabel; + QListWidget* bookList; + QListWidget* historyList; + QPushButton* terminateClientButton; + QPushButton* terminateHostButton; + + QTimer* clientTimer; + QLabel* timerLabel; +}; diff --git a/AsanovDamir/laba2/HostUtils/hostProcessing.hpp b/AsanovDamir/laba2/HostUtils/hostProcessing.hpp new file mode 100644 index 0000000..9ff5353 --- /dev/null +++ b/AsanovDamir/laba2/HostUtils/hostProcessing.hpp @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include "HostWindow.hpp" +#include "conn_sock.hpp" +#include "semaphore.hpp" +#include "logger.hpp" +#include "Book.hpp" +#include + +bool takeBook(std::vector& books, const std::string& bookName, LoggerHost& logger) { + for (auto& book : books) { + if (book.name == bookName) { + if (book.count > 0) { + book.count--; + logger.log(Status::INFO, "Book taken: " + bookName); + return true; + } else { + logger.log(Status::INFO, "Book not available: " + bookName); + return false; + } + } + } + + logger.log(Status::INFO, "No such Book in library: " + bookName); + return false; +} + +bool returnBook(std::vector& books, const std::string& bookName, LoggerHost& logger) { + for (auto& book : books) { + if (book.name == bookName) { + book.count++; + logger.log(Status::INFO, "Book returned: " + bookName); + return true; + } + } + + logger.log(Status::INFO, "No such Book in library: " + bookName); + return false; +} + +void listenForClientMessages(conn& conn, Semaphore& semaphore, std::vector& books, HostWindow& window, std::atomic& is_running) { + auto& logger = LoggerHost::get_instance(); + + while (is_running) { + semaphore.Wait(); // Start critical section + + char buffer[1024] = {0}; + if (conn.Read(buffer, sizeof(buffer))) { + std::string request(buffer); + logger.log(Status::INFO, "Request is recieved: " + request); + + if (request.rfind("TAKE ", 0) == 0) { + std::string bookName = request.substr(5); + bool res = takeBook(books, bookName, logger); + if (res) + { + window.signalStopTimer(); + std::string response = "YES"; + if (conn.Write(response.c_str(), response.size())) { + logger.log(Status::INFO, "Host response successfully"); + } + else { + logger.log(Status::ERROR, "Failed to response"); + } + } + else + { + window.signalResetTimer(); + std::string response = "NO"; + if (conn.Write(response.c_str(), response.size())) { + logger.log(Status::INFO, "Host response successfully"); + } + else { + logger.log(Status::ERROR, "Failed to response"); + } + } + + window.addHistory("TAKE book: ", QString::fromStdString(bookName), res); + } else if (request.rfind("RETURN ", 0) == 0) { + window.signalResetTimer(); + std::string bookName = request.substr(7); + bool res = returnBook(books, bookName, logger); // client unexpected any response on this request + + window.addHistory("RETURN book: ", QString::fromStdString(bookName), res); + } + + window.updateBooks(books); + } + + semaphore.Post(); // End critical section + sleep(0.01); + } +} + +int processHost(const std::string& hostTitle, Semaphore& semaphore, conn& conn, std::vector books, QApplication& app, pid_t pid) { + LoggerHost::get_instance().log(Status::INFO, "Host is running"); + std::atomic is_running(true); + HostWindow window(hostTitle, books); + window.clientPid = pid; + + // Start host's listenning in new thread + std::thread listenerThread(listenForClientMessages, std::ref(conn), std::ref(semaphore), std::ref(books), std::ref(window), std::ref(is_running)); + + window.show(); + int result = app.exec(); // Start window in this thread + + // Complete thread with listenning socket + is_running = false; + if (listenerThread.joinable()) { + listenerThread.join(); + } + + return result; +} diff --git a/AsanovDamir/laba2/README.md b/AsanovDamir/laba2/README.md new file mode 100644 index 0000000..ec0b2e9 --- /dev/null +++ b/AsanovDamir/laba2/README.md @@ -0,0 +1,31 @@ +# Проект: Demon + +# Автор + +Асанов Дамир Русланович 5030102/10201 Лабораторная работа №2 вариант №23: + +## Описание + +Родственные. Один к одному. 4, 6, 7 + +Библиотека. Хост - библиотека, клиенты - читатели. Хост по запросу выдает книги (при наличии их в библиотеке), либо говорит что +книга занята/отсутсвует и получает их обратно. Клиенты запрашивают интересующие их книги, читают их какое-то время и возвращают +в библиотеку (скорость чтения случайна и линейна во времени). Статус всех книг библиотеки (название, сколько доступно свободных +копий, кем и когда взята) отражаются в графическом интерфейсе. В интерфейсе клиентов их история запросов (когда и какие книги +они брали и возвращали). + +4. Очереди сообщений (mq_open). TYPE_CODE - mq. Работа с очередью должна производиться в каждом процессе уже после создания +дочернего (так, будто это несвязанные процессы). + +6. Именованные каналы (mkfifo). TYPE_CODE - fifo. Работа с именованным каналом должна производиться в каждом процессе уже +после создания дочернего (так, будто это несвязанные процессы). После окончания работы родительский процесс должен удалять +файл именованного канала. + +7. Сокеты (socket). TYPE_CODE – sock + +## Запуск + +введите команду ```bash build.sh```, чтобы собрать проект +введите команду ```bash runHostSock.sh```, чтобы запустить сокетное взаимодействие +введите команду ```bash runHostMq.sh```, чтобы запустить очередь сообщений взаимодействие +введите команду ```bash runHostFifo.sh```, чтобы запустить именнованный канал взаимодействие diff --git a/AsanovDamir/laba2/build.sh b/AsanovDamir/laba2/build.sh new file mode 100644 index 0000000..a90ec84 --- /dev/null +++ b/AsanovDamir/laba2/build.sh @@ -0,0 +1,16 @@ +#!/bin/bash +mkdir -p build +cd build +cmake .. +make + +find . -type f -name '*.o' -delete +find . -type f -name '*.cmake' -delete +find . -type f -name 'CMakeCache.txt' -delete +find . -type f -name 'Makefile' -delete +rm -rf CMakeFiles +rm -rf host_sock_autogen +rm -rf host_mq_autogen +rm -rf host_fifo_autogen +rm -rf ClientUtils +rm -rf HostUtils diff --git a/AsanovDamir/laba2/conn.hpp b/AsanovDamir/laba2/conn.hpp new file mode 100644 index 0000000..306846a --- /dev/null +++ b/AsanovDamir/laba2/conn.hpp @@ -0,0 +1,11 @@ +#pragma once + +class conn { +public: + virtual ~conn() {}; + + virtual bool Read(void* buf, size_t count) = 0; + virtual bool Write(const void* buf, size_t count) = 0; + + virtual bool IsInitialized() const = 0; +}; diff --git a/AsanovDamir/laba2/conn_fifo.cpp b/AsanovDamir/laba2/conn_fifo.cpp new file mode 100644 index 0000000..69f2bcb --- /dev/null +++ b/AsanovDamir/laba2/conn_fifo.cpp @@ -0,0 +1,83 @@ +#include "conn_fifo.hpp" + +ConnFifo::ConnFifo(const std::string& fifoPath, LoggerHost& logger) : path(fifoPath), readFd(-1), writeFd(-1) { + + if (mkfifo(path.c_str(), 0666) == -1) { + if (errno != EEXIST) { + logger.log(Status::ERROR, "Failed to create FIFO"); + return; + } + } + + readFd = open(path.c_str(), O_RDONLY | O_NONBLOCK); // open for read + if (readFd == -1) { + logger.log(Status::ERROR, "Failed to open FIFO for reading"); + return; + } + + writeFd = open(path.c_str(), O_WRONLY); // open for write + if (writeFd == -1) { + logger.log(Status::ERROR, "Failed to open FIFO for writing"); + close(readFd); + return; + } +} + +ConnFifo::ConnFifo(const std::string& fifoPath, LoggerClient& logger) : path(fifoPath), readFd(-1), writeFd(-1) { + + readFd = open(path.c_str(), O_RDONLY | O_NONBLOCK); // open for read + if (readFd == -1) { + logger.log(Status::ERROR, "Failed to open FIFO for reading"); + return; + } + + writeFd = open(path.c_str(), O_WRONLY); // open for write + if (writeFd == -1) { + logger.log(Status::ERROR, "Failed to open FIFO for writing"); + close(readFd); + return; + } +} + +bool ConnFifo::IsInitialized() const { + return writeFd != -1 && readFd != -1; +} + +ConnFifo::~ConnFifo() { + if (readFd != -1) { + close(readFd); + } + if (writeFd != -1) { + close(writeFd); + } + + if (!path.empty()) { + unlink(path.c_str()); + } +} + +bool ConnFifo::Read(void* buf, size_t count) { + if (!IsInitialized()) { + return false; + } + + ssize_t bytesRead = read(readFd, buf, count); + if (bytesRead == -1) { + return false; + } + + return bytesRead > 0; +} + +bool ConnFifo::Write(const void* buf, size_t count) { + if (!IsInitialized()) { + return false; + } + + ssize_t bytesWritten = write(writeFd, buf, count); + if (bytesWritten == -1) { + return false; + } + + return static_cast(bytesWritten) == count; +} diff --git a/AsanovDamir/laba2/conn_fifo.hpp b/AsanovDamir/laba2/conn_fifo.hpp new file mode 100644 index 0000000..113011a --- /dev/null +++ b/AsanovDamir/laba2/conn_fifo.hpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include "conn.hpp" +#include "logger.hpp" + +class ConnFifo : public conn { +public: + ConnFifo(const std::string& fifoPath, LoggerHost& logger); + ConnFifo(const std::string& fifoPath, LoggerClient& logger); + ~ConnFifo() override; + + bool Read(void* buf, size_t count) override; + bool Write(const void* buf, size_t count) override; + + bool IsInitialized() const override; + +private: + std::string path; + int readFd; + int writeFd; +}; diff --git a/AsanovDamir/laba2/conn_mq.cpp b/AsanovDamir/laba2/conn_mq.cpp new file mode 100644 index 0000000..89e7d07 --- /dev/null +++ b/AsanovDamir/laba2/conn_mq.cpp @@ -0,0 +1,76 @@ +#include "conn_mq.hpp" +#include +#include +#include + +ConnMq::ConnMq(key_t key, LoggerHost& logger) : queueKey(key), isHost(true) +{ + int flags = IPC_CREAT | 0666; + queueId = msgget(key, flags); + + if (queueId == -1) { + logger.log(Status::ERROR, "Failed to open message queue"); + } +} + +ConnMq::ConnMq(key_t key, LoggerClient& logger) : queueKey(key), isHost(false) +{ + int flags = 0666; + queueId = msgget(key, flags); + + if (queueId == -1) { + logger.log(Status::ERROR, "Failed to open message queue"); + } +} + +ConnMq::~ConnMq() { + if (IsInitialized()) { + msgctl(queueId, IPC_RMID, nullptr); // TODO: some check + } +} + +bool ConnMq::Read(void* buf, size_t count) { + if (!IsInitialized()) { + return false; + } + + struct msgbuf { + long mtype; + char mtext[1024]; + } message; + + ssize_t bytesRead = msgrcv(queueId, &message, sizeof(message.mtext), !isHost + 1, 0); // !isHost: if host -> read client else read host + if (bytesRead == -1) { + return false; + } + + std::memcpy(buf, message.mtext, std::min(count, static_cast(bytesRead))); + return true; +} + +bool ConnMq::Write(const void* buf, size_t count) { + if (!IsInitialized()) { + return false; + } + + struct msgbuf { + long mtype; + char mtext[1024]; + } message; + + message.mtype = isHost + 1; // if host -> write as host (+ 1 because mtype > 0) + if (count > sizeof(message.mtext)) { + return false; + } + + std::memcpy(message.mtext, buf, count); + if (msgsnd(queueId, &message, count, 0) == -1) { + return false; + } + + return true; +} + +bool ConnMq::IsInitialized() const { + return (queueId != -1); +} diff --git a/AsanovDamir/laba2/conn_mq.hpp b/AsanovDamir/laba2/conn_mq.hpp new file mode 100644 index 0000000..115b74c --- /dev/null +++ b/AsanovDamir/laba2/conn_mq.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include "conn.hpp" +#include "logger.hpp" + +class ConnMq : public conn { +public: + ConnMq(key_t key, LoggerHost& logger); + ConnMq(key_t key, LoggerClient& logger); + ~ConnMq() override; + + bool Read(void* buf, size_t count) override; + bool Write(const void* buf, size_t count) override; + + bool IsInitialized() const override; + +private: + key_t queueKey; + int queueId; + bool isHost; // for read/write msg type +}; diff --git a/AsanovDamir/laba2/conn_sock.cpp b/AsanovDamir/laba2/conn_sock.cpp new file mode 100644 index 0000000..172ae76 --- /dev/null +++ b/AsanovDamir/laba2/conn_sock.cpp @@ -0,0 +1,109 @@ +#include "conn_sock.hpp" +#include +#include +#include +#include + +ConnSock::ConnSock(int hostPort, LoggerHost& logger) : sock_fd(-1) { + logger.log(Status::INFO, "Creating socket for Host"); + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (sock_fd == -1) { + logger.log(Status::ERROR, "Socket creation failed"); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(hostPort); + addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + logger.log(Status::ERROR, "Bind failed"); + close(sock_fd); + sock_fd = -1; + return; + } + + if (listen(sock_fd, 1) == -1) { + logger.log(Status::ERROR, "Listen failed"); + close(sock_fd); + sock_fd = -1; + return; + } + + logger.log(Status::INFO, "Socket successfully created and listening"); +} + +ConnSock::ConnSock(int hostPort, LoggerClient& logger) : sock_fd(-1) { + logger.log(Status::INFO, "Creating socket for Client"); + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (sock_fd == -1) { + logger.log(Status::ERROR, "Socket creation failed"); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(hostPort); + + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) <= 0) { + logger.log(Status::ERROR, "Invalid host IP address"); + close(sock_fd); + sock_fd = -1; + return; + } + + if (connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + logger.log(Status::ERROR, "Connect failed"); + close(sock_fd); + sock_fd = -1; + return; + } + + logger.log(Status::INFO, "Connected to host successfully"); +} + +ConnSock* ConnSock::Accept(LoggerHost& logger) { + struct sockaddr_in client_addr; + socklen_t client_len = sizeof(client_addr); + int client_fd = accept(sock_fd, (struct sockaddr*)&client_addr, &client_len); + if (client_fd == -1) { + logger.log(Status::ERROR, "Accept failed"); + return nullptr; + } + + logger.log(Status::INFO, "Client connection accepted"); + // make new socket for communication with client + ConnSock* conn = new ConnSock(); + conn->sock_fd = client_fd; + conn->addr = client_addr; + return conn; +} + +bool ConnSock::Read(void* buf, size_t count) { + if (!IsInitialized()) { + return false; + } + + ssize_t bytes_read = recv(sock_fd, buf, count, 0); + if (bytes_read <= 0) { + return false; + } + return true; +} + +bool ConnSock::Write(const void* buf, size_t count) { + if (!IsInitialized()) { + return false; + } + + ssize_t bytes_sent = send(sock_fd, buf, count, 0); + if (bytes_sent <= 0) { + return false; + } + return true; +} + +ConnSock::~ConnSock() { + if (sock_fd != -1) { + close(sock_fd); + } +} diff --git a/AsanovDamir/laba2/conn_sock.hpp b/AsanovDamir/laba2/conn_sock.hpp new file mode 100644 index 0000000..eedfbdf --- /dev/null +++ b/AsanovDamir/laba2/conn_sock.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include "logger.hpp" +#include "conn.hpp" + +class ConnSock : public conn { +public: + ConnSock(int hostPort, LoggerHost& logger); + ConnSock(int hostPort, LoggerClient& logger); + ~ConnSock(); + + ConnSock* Accept(LoggerHost& logger); // only for host + + bool Read(void* buf, size_t count); + bool Write(const void* buf, size_t count); + + bool IsInitialized() const { + return sock_fd != -1; + } + +private: + int sock_fd; // Socket file Descriptor + struct sockaddr_in addr; // socket address + + ConnSock() = default; // for accept +}; diff --git a/AsanovDamir/laba2/host_fifo.cpp b/AsanovDamir/laba2/host_fifo.cpp new file mode 100644 index 0000000..c30a84b --- /dev/null +++ b/AsanovDamir/laba2/host_fifo.cpp @@ -0,0 +1,53 @@ +#include +#include "HostUtils/HostWindow.hpp" +#include "HostUtils/hostProcessing.hpp" +#include "ClientUtils/clientProcessing.hpp" +#include "conn_fifo.hpp" +#include "semaphore.hpp" +#include "logger.hpp" + +int main(int argc, char* argv[]) { + + // initialize some stuff + std::vector books = { + {"Book 1", 10}, + {"Book 2", 5}, + {"Book 3", 20}, + {"Book 4", 0} + }; + Semaphore semaphore(1); + + auto& logger = LoggerHost::get_instance(); + + ConnFifo hostFifo("/tmp/my_fifo", logger); + if (!hostFifo.IsInitialized()) { + logger.log(Status::ERROR, "Failed to initialize host queue"); + return EXIT_FAILURE; + } + + // Make child process + pid_t pid = fork(); + if (pid == -1) { + + // Error + logger.log(Status::ERROR, "Failed to fork client"); + return EXIT_FAILURE; + + } else if (pid == 0) { + + // this is child process -> start client + ConnFifo clientFifo("/tmp/my_fifo", LoggerClient::get_instance()); + if (!clientFifo.IsInitialized()) { + logger.log(Status::ERROR, "Failed to initialize client queue"); + return EXIT_FAILURE; + } + return processClient(semaphore, clientFifo, books); + + } else { + + // this is main process -> start host + QApplication app(argc, argv); + return processHost("Communication by queue", semaphore, hostFifo, books, app, pid); + + } +} diff --git a/AsanovDamir/laba2/host_mq.cpp b/AsanovDamir/laba2/host_mq.cpp new file mode 100644 index 0000000..84aa3b2 --- /dev/null +++ b/AsanovDamir/laba2/host_mq.cpp @@ -0,0 +1,56 @@ +#include +#include "HostUtils/HostWindow.hpp" +#include "HostUtils/hostProcessing.hpp" +#include "ClientUtils/clientProcessing.hpp" +#include "conn_mq.hpp" +#include "semaphore.hpp" +#include "logger.hpp" +#include +#include + +int main(int argc, char* argv[]) { + + // initialize some stuff + key_t key = ftok("LALALA", 65); + std::vector books = { + {"Book 1", 10}, + {"Book 2", 5}, + {"Book 3", 20}, + {"Book 4", 0} + }; + Semaphore semaphore(1); + + auto& logger = LoggerHost::get_instance(); + + ConnMq hostMq(key, logger); + if (!hostMq.IsInitialized()) { + logger.log(Status::ERROR, "Failed to initialize host queue"); + return EXIT_FAILURE; + } + + // Make child process + pid_t pid = fork(); + if (pid == -1) { + + // Error + logger.log(Status::ERROR, "Failed to fork client"); + return EXIT_FAILURE; + + } else if (pid == 0) { + + // this is child process -> start client + ConnMq clientMq(key, LoggerClient::get_instance()); + if (!clientMq.IsInitialized()) { + logger.log(Status::ERROR, "Failed to initialize client queue"); + return EXIT_FAILURE; + } + return processClient(semaphore, clientMq, books); + + } else { + + // this is main process -> start host + QApplication app(argc, argv); + return processHost("Communication by queue", semaphore, hostMq, books, app, pid); + + } +} diff --git a/AsanovDamir/laba2/host_sock.cpp b/AsanovDamir/laba2/host_sock.cpp new file mode 100644 index 0000000..d89150a --- /dev/null +++ b/AsanovDamir/laba2/host_sock.cpp @@ -0,0 +1,72 @@ +#include +#include "HostUtils/HostWindow.hpp" +#include "HostUtils/hostProcessing.hpp" +#include "ClientUtils/clientProcessing.hpp" +#include "conn_sock.hpp" +#include "semaphore.hpp" +#include "logger.hpp" +#include +#include + +int main(int argc, char* argv[]) { + + // take port + if (argc != 2) { + std::cerr << "Usage: ./host " << std::endl; + return EXIT_FAILURE; + } + + // initialize some stuff + int port = std::stoi(argv[1]); + std::vector books = { + {"Book 1", 10}, + {"Book 2", 5}, + {"Book 3", 20}, + {"Book 4", 0} + }; + Semaphore semaphore(1); + + auto& logger = LoggerHost::get_instance(); + + // Create hostSocket for incomming connection requests from client + ConnSock hostSocket(port, logger); + if (!hostSocket.IsInitialized()) { + logger.log(Status::ERROR, "Failed to initialize host socket"); + return EXIT_FAILURE; + } + + // Make child process + pid_t pid = fork(); + if (pid == -1) { + + // Error + logger.log(Status::ERROR, "Failed to fork client"); + return EXIT_FAILURE; + + } else if (pid == 0) { + + // this is child process -> start client + ConnSock clientSocket(port, LoggerClient::get_instance()); + if (!clientSocket.IsInitialized()) { + logger.log(Status::ERROR, "Failed to initialize client socket"); + return EXIT_FAILURE; + } + return processClient(semaphore, clientSocket, books); + + } else { + + // this is main process -> start host + ConnSock* conn = hostSocket.Accept(logger); + if (!conn) + { + logger.log(Status::ERROR, "Failed to accept connection"); + return EXIT_FAILURE; + } + + QApplication app(argc, argv); + int res = processHost("Host Port: " + std::string(argv[1]), semaphore, *conn, books, app, pid); + delete conn; + return res; + + } +} diff --git a/AsanovDamir/laba2/logger.hpp b/AsanovDamir/laba2/logger.hpp new file mode 100644 index 0000000..65d2e86 --- /dev/null +++ b/AsanovDamir/laba2/logger.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +enum class Status : bool +{ +INFO, +ERROR +}; + +class LoggerClient +{ +public: + static LoggerClient& get_instance() + { + static LoggerClient instance; + return instance; + } + + void log(Status status, const std::string& message) + { + if (status == Status::INFO) + std::cout << "[CLIENT][INFO] " << message << std::endl; + else + std::cout << "[CLIENT][ERROR] " << message << std::endl; + } + +private: + LoggerClient() = default; + LoggerClient(const LoggerClient&) = delete; + LoggerClient& operator=(const LoggerClient&) = delete; +}; + +class LoggerHost +{ +public: + static LoggerHost& get_instance() + { + static LoggerHost instance; + return instance; + } + + void log(Status status, const std::string& message) + { + if (status == Status::INFO) + std::cout << "[HOST][INFO] " << message << std::endl; + else + std::cout << "[HOST][ERROR] " << message << std::endl; + } + +private: + LoggerHost() = default; + LoggerHost(const LoggerHost&) = delete; + LoggerHost& operator=(const LoggerHost&) = delete; +}; diff --git a/AsanovDamir/laba2/runHostFifo.sh b/AsanovDamir/laba2/runHostFifo.sh new file mode 100644 index 0000000..d69c25f --- /dev/null +++ b/AsanovDamir/laba2/runHostFifo.sh @@ -0,0 +1,2 @@ +#!/bin/bash +./build/host_fifo diff --git a/AsanovDamir/laba2/runHostMq.sh b/AsanovDamir/laba2/runHostMq.sh new file mode 100644 index 0000000..e21d592 --- /dev/null +++ b/AsanovDamir/laba2/runHostMq.sh @@ -0,0 +1,2 @@ +#!/bin/bash +./build/host_mq diff --git a/AsanovDamir/laba2/runHostSock.sh b/AsanovDamir/laba2/runHostSock.sh new file mode 100644 index 0000000..303f51c --- /dev/null +++ b/AsanovDamir/laba2/runHostSock.sh @@ -0,0 +1,2 @@ +#!/bin/bash +./build/host_sock 10107 diff --git a/AsanovDamir/laba2/semaphore.cpp b/AsanovDamir/laba2/semaphore.cpp new file mode 100644 index 0000000..9277f64 --- /dev/null +++ b/AsanovDamir/laba2/semaphore.cpp @@ -0,0 +1,32 @@ +#include "semaphore.hpp" +#include "logger.hpp" +#include +#include + +Semaphore::Semaphore(unsigned int value) { + if (sem_init(&semaphore, 0, value) == -1) { + LoggerHost::get_instance().log(Status::ERROR, "Semaphore initialization failed"); + } +} + +bool Semaphore::Wait() { + if (sem_wait(&semaphore) == -1) { + LoggerHost::get_instance().log(Status::ERROR, "Semaphore wait failed"); + return false; + } + return true; +} + +bool Semaphore::Post() { + if (sem_post(&semaphore) == -1) { + LoggerHost::get_instance().log(Status::ERROR, "Semaphore post failed"); + return false; + } + return true; +} + +Semaphore::~Semaphore() { + if (sem_destroy(&semaphore) == -1) { + LoggerHost::get_instance().log(Status::ERROR, "Semaphore destruction failed"); + } +} diff --git a/AsanovDamir/laba2/semaphore.hpp b/AsanovDamir/laba2/semaphore.hpp new file mode 100644 index 0000000..38cc089 --- /dev/null +++ b/AsanovDamir/laba2/semaphore.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +class Semaphore { +public: + Semaphore(unsigned int value); + bool Wait(); + bool Post(); + ~Semaphore(); + +private: + sem_t semaphore; +};