Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СПОВМ_курсач_26 / 890541_Kalenchits_K_V_Poyasnitelnaya_zapiska.docx
Скачиваний:
2
Добавлен:
29.07.2022
Размер:
109.01 Кб
Скачать

Список использованных источников

1. Павловская, Татьяна. С/С++. Программирование на языке высокого уровня. – СПб.: Питер, 2019. – 460 с.

2. Щупак Ю. А. Win32 API. Разработка приложения для Windows. – СПб.: Питер, 2010. – 590 с.

3. SetupDiGetClassDevs function (setupapi.h) - Win32 apps | Microsoft Docs. Режим доступа: https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsw. Дата доступа: 03.05.2021 г.

4. SetupDiEnumDeviceInfo function (setupapi.h) - Win32 apps | Microsoft Docs. Режим доступа: https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdienumdeviceinfo. Дата доступа: 03.05.2021 г.

5. SetupDiGetClassDescription function (setupapi.h) - Win32 apps | Microsoft Docs. Режим доступа: https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdescription. Дата доступа: 03.05.2021 г.

6. SetupDiGetDeviceProperty function (setupapi.h) - Win32 apps | Microsoft Docs. Режим доступа: https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdigetdeviceproperty. Дата доступа: 03.05.2021 г.

Приложение а Листинг программы

Листинг реализации основного модуля программы (main.cpp)

#include <QList>

#include <QTextStream>

#include <QtWidgets>

#include <iostream>

#include <unistd.h>

#include <widget.hpp>

const size_t SLEEP_DUR = 10; // ms

int main(int argc, char **argv) {

QApplication app(argc, argv);

GUI window;

window.setWindowTitle("Отслеживание директории");

window.resize(800, 400);

window.show();

int i = 0;

while (true && window.isVisible()) {

app.processEvents();

if (i % 50 == 0) { // Раз в полсекунды

window.render();

i = 0;

}

i++;

usleep(SLEEP_DUR * 1000);

}

app.exit();

return 0;

}

Листинг реализации модуля программы (monitor.hpp)

#pragma once

#include <files_monitor.hpp>

#include <fstream>

#include <iostream>

#include <memory>

#include <string>

#include <sys/inotify.h>

#include <unordered_map>

#include <unordered_set>

using std::string;

using table_record = std::vector<QString>;

using table_records = std::vector<table_record>;

table_record gen_item(string path, string event) {

table_record rec;

rec.push_back(QString::fromStdString(path));

rec.push_back(QString::fromStdString(event));

return rec;

}

Syslog<> logger;

/// Этот класс отвечает

class Tracker {

public:

table_records get_records() {

table_records events;

while (true) {

try {

const inotify_event *eventp = inotify->read(1);

if (eventp == nullptr) {

break;

}

if (eventp->name != NULL && eventp->len > 1) { // Убрать метаобращения

events.push_back(gen_item(eventp->name, decode_event(eventp->mask)));

}

} catch (std::system_error &error) {

std::cout << "Error: " << error.code() << " - " << error.what() << '\n';

}

}

reverse(

events.begin(),

events.end()); // Последние события должны быть вверху таблицы (первые)

return events;

}

void change_dir(std::string path) {

inotify.reset(new Inotify<>(logger));

inotify->add_watch(path);

}

private:

string decode_event(uint32_t mask) {

if (IN_ACCESS & mask)

return "К файлу получили доступ";

if (IN_MODIFY & mask)

return "Файл был изменён";

if (IN_ATTRIB & mask)

return "Метадата файла была изменена";

if (IN_CLOSE & mask)

return "Файл был закрыт";

if (IN_OPEN & mask)

return "Файл был открыт";

if (IN_MOVE & mask)

return "Файл был перемещён";

if (IN_CREATE & mask)

return "Файл был создан";

if (IN_DELETE & mask)

return "Файл был удалён";

std::stringstream stream;

stream << "Неизвестное событие. Маска: ";

stream << std::hex << mask;

return stream.str();

}

std::unique_ptr<Inotify<>> inotify;

};

Листинг реализации модуля программы (widget.hpp)

#pragma once

#include <QAbstractTableModel>

#include <QFont>

#include <QLabel>

#include <QTableView>

#include <QVBoxLayout>

#include <QWidget>

#include <memory>

#include <monitor.hpp>

#include <sys/resource.h>

#include <sys/time.h>

class MyTable : public QAbstractTableModel {

public:

MyTable(QObject *parent = nullptr) : QAbstractTableModel(parent) {}

void add_events(table_records &events) {

beginInsertRows(QModelIndex(), recs.size(),

recs.size() + events.size() - 1);

recs.insert(recs.begin(), events.begin(), events.end());

endInsertRows();

}

void reset() {

beginResetModel();

this->recs.clear();

endResetModel();

}

private:

int rowCount(const QModelIndex &parent) const {

if (parent.isValid())

return 0;

return recs.size();

}

int columnCount(const QModelIndex &parent) const {

if (parent.isValid())

return 0;

return 2;

}

QVariant data(const QModelIndex &index, int role) const {

if (!index.isValid() || recs.empty())

return QVariant();

if (role == Qt::DisplayRole)

return recs.at(index.row()).at(index.column());

return QVariant();

}

QVariant headerData(int section, Qt::Orientation orientation,

int role) const {

if ((role == Qt::DisplayRole || role == Qt::ToolTipRole)) {

if (orientation == Qt::Horizontal) {

switch (section) {

case 0:

return (QString("FILE"));

case 1:

return (QString("EVENT"));

}

}

}

return QAbstractItemModel::headerData(section, orientation, role);

}

table_records recs;

};

class GUI : public QWidget {

public:

GUI() {

choose_dir_button =

std::make_unique<QPushButton>("Выбрать директорию", this);

connect(choose_dir_button.get(), &QPushButton::released, this,

&GUI::change_dir_click);

cur_dir = std::make_unique<QLineEdit>(this);

cur_dir->setReadOnly(true);

cur_dir->setPlaceholderText({"Выберите директорию для отслеживания"});

myModel = std::make_unique<MyTable>();

table = std::make_unique<QTableView>(this);

table->setModel(myModel.get());

table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

table->show();

vbox = std::make_unique<QVBoxLayout>(this);

vbox->addWidget(table.get());

vbox->addWidget(choose_dir_button.get());

vbox->addWidget(cur_dir.get());

setLayout(vbox.get());

}

void render() {

if (!dir.empty()) {

auto recs = tracker.get_records();

myModel->add_events(recs);

}

}

private:

void change_dir_click() {

QFileDialog dialog(this);

dialog.setFileMode(QFileDialog::Directory);

dialog.setOption(QFileDialog::ShowDirsOnly, true);

int result = dialog.exec();

QString directory;

if (result) {

myModel->reset();

directory = dialog.selectedFiles()[0];

cur_dir->setText(directory);

dir = directory.toUtf8().constData();

tracker.change_dir(dir);

}

}

Tracker tracker;

std::unique_ptr<MyTable> myModel;

std::unique_ptr<QTableView> table;

std::unique_ptr<QPushButton> choose_dir_button;

std::unique_ptr<QVBoxLayout> vbox;

std::unique_ptr<QLineEdit> cur_dir;

std::string dir = "";

};

Листинг реализации модуля программы (syslog.hpp)

// Wrapper for syslog() system call in C++

// 01/20/18, dzchoi, feature complete

// How to use:

// - Define a global (or local) instance of Syslog object:

// Syslog<LOG_INFO> log;

// Syslog<> log; (setting LOG_ERR as a default priority)

// Syslog<> log("backupd", LOG_PID, LOG_DAEMON);

// - Write a log:

// log(LOG_WARNING, "errno=%d, strerror(errno)=%m", errno);

// log("..."); (using the default priority and the default

// facility) log(LOG_DAEMON, "..."); (using the default priority)

// log(LOG_INFO|LOG_DAEMON, "...");

// See http://hant.ask.helplib.com/c++/post_707188 for wrapping with the error

// stream

#ifndef SYSLOG_HPP

#define SYSLOG_HPP

#include <cstdarg> // va_list, va_start(), va_end()

extern "C" {

#include <syslog.h> // LOG_*, openlog(), closelog(), vsyslog(), setlogmask()

}

template <int Priority = LOG_ERR>

// Priority is the default priority to log with.

class Syslog { // being declared as a function object

// Should be used to define a single global object (though not forced), since

// the C openlog() keeps all its settings (ident, option, and facility, but

// not Priority) globally.

public:

Syslog(const char *ident, int option = LOG_ODELAY, int facility = LOG_USER) {

// The contents pointed to by ident is not backed up, so assumed to be

// unchanged across the entire program.

openlog(ident, option, facility);

}

Syslog() {}

// does not call openlog(), retaining any previous settings of openlog() from

// when constructing the last Syslog<> object.

~Syslog() { closelog(); }

void operator()(int priority, const char *format...) const;

// Note, the priority argument is formed by ORing together a facility value

// (like LOG_AUTH) and a level value (like LOG_ERR). If no facility value is

// ORed into the priority, then the default value set by openlog() is used,

// or, if there was no preceding openlog() call, a default of LOG_USER is

// employed.

template <typename... Args> // uses the default Priority

void operator()(const char *format, Args... args) const {

operator()(Priority, format, args...);

}

int setlogmask(int mask) { return ::setlogmask(mask); }

};

template <int Priority>

void Syslog<Priority>::operator()(int priority, const char *format...) const {

if (LOG_PRI(priority) == 0)

priority |= LOG_PRI(Priority);

va_list ap; // arg startup

va_start(ap, format);

vsyslog(priority, format, ap);

va_end(ap); // arg cleanup

}

#endif /* SYSLOG_HPP */

Листинг реализации модуля программы (files_monitor.hpp)

#ifndef INOTIFY_HPP

#define INOTIFY_HPP

#include "syslog.hpp"

#include <algorithm>

#include <chrono>

#include <cstring>

#include <experimental/filesystem>

#include <string>

#include <system_error>

#include <unordered_map>

#ifdef DEBUG

#include <cstdio>

#endif

extern "C" {

#include <poll.h>

#include <sys/inotify.h>

#include <unistd.h>

}

namespace fs = std::experimental::filesystem;

#ifdef DEBUG

#define printf(...) std::printf(__VA_ARGS__)

#else

#define printf(...)

#endif

template <typename CharT, typename Traits, typename Alloc>

inline std::basic_string<CharT, Traits, Alloc>

operator/(const std::basic_string<CharT, Traits, Alloc> &path1,

const CharT *path2)

{

return (fs::path(path1) / fs::path(path2)).string();

}

template <typename Log = Syslog<LOG_ERR>>

class Inotify {

const Log &log;

const int fd;

pollfd fds;

uint32_t mask;

struct Watch {

std::string path;

bool in_move;

};

std::unordered_map<int, Watch> watches;

inotify_event

buffer[(4 * 1024 + sizeof(inotify_event) - 1) / sizeof(inotify_event)];

int bytes_in_buffer = 0;

int bytes_handled = 0;

public:

Inotify(const Log &log, uint32_t mask = IN_ALL_EVENTS)

: log{log}, fd{inotify_init1(IN_NONBLOCK)}, fds{fd, POLLIN}, mask{mask} {

if (fd == -1)

throw std::system_error(errno, std::system_category());

}

~Inotify() { close(fd); }

const std::string &path(int wd) const { return watches.at(wd).path; }

int add_watch(const std::string &, bool = true);

void rm_watch(int wd) noexcept;

void rm_all_watches() noexcept;

const inotify_event *read(int timeout = (-1), int read_delay = 0);

};

template <typename Log>

int Inotify<Log>::add_watch(const std::string &path, bool in_move)

{

const bool recursive = path.back() != '/';

const int wd =

inotify_add_watch(fd, path.c_str(),

mask | IN_ONLYDIR | IN_MOVE_SELF |

(recursive ? IN_CREATE | IN_MOVED_TO : 0));

if (wd == -1) {

log("Warning: Cannot watch \"%s\": %s", path.c_str(), std::strerror(errno));

return wd;

}

const auto it = watches.find(wd);

if (it == watches.end()) {

printf("[%d] %s created\n", wd, path.c_str());

watches.emplace(wd, Watch{path, false});

}

else {

const std::string &path0 = it->second.path;

const auto &diff = std::mismatch(path0.begin(), path0.end(), path.begin());

if (diff.first == path0.end() &&

(diff.second == path.end() ||

!recursive && diff.second + 1 == path.end())) {

printf("[%d] %s ignored as a duplicate\n", wd, path.c_str());

return wd;

}

printf("[%d] %s %s\n", wd, path.c_str(),

diff.second == path.end() && diff.first + 1 == path0.end() &&

*diff.first == '/'

? "changed to recursive"

: "moved");

it->second.path = path;

}

if (in_move) {

if (recursive)

for (const fs::path &subdir : fs::directory_iterator(path))

if (fs::is_directory(subdir))

add_watch(subdir.string(), true);

}

else {

for (const fs::path &path : fs::directory_iterator(path))

if (!fs::is_other(path)) {

const bool isdir = fs::is_directory(path);

if (mask & IN_CREATE || isdir && recursive) {

const std::string &name = path.filename().string();

const uint32_t len =

(name.size() + sizeof(int)) / sizeof(int) * sizeof(int);

inotify_event &event =

*(inotify_event *)((char *)buffer + bytes_in_buffer);

bytes_in_buffer += sizeof(inotify_event) + len;

if (bytes_in_buffer > sizeof(buffer)) {

log("Error: add_watch() - Buffer underruns");

throw std::system_error(EINVAL, std::system_category());

}

event.wd = wd;

event.mask = isdir ? IN_ISDIR | IN_CREATE : IN_CREATE;

event.cookie = 0;

event.len = len;

name.copy(event.name, len);

event.name[name.size()] = '\0';

}

}

}

return wd;

}

template <typename Log>

void Inotify<Log>::rm_watch(int wd) noexcept

{

if (inotify_rm_watch(fd, wd))

log("Warning: inotify_rm_watch():%d - %s", errno, std::strerror(errno));

}

template <typename Log>

void Inotify<Log>::rm_all_watches() noexcept

{

for (const auto &it : watches)

rm_watch(it.first);

}

template <typename Log>

const inotify_event *Inotify<Log>::read(int timeout, int read_delay)

{

const auto then = std::chrono::system_clock::now();

if (bytes_in_buffer == 0) {

const char *where;

where = "poll()";

switch (poll(&fds, 1, timeout)) {

case 0:

return nullptr;

default:

where = "usleep()";

if (usleep(read_delay * 1000u) != -1) {

where = "read()";

bytes_in_buffer = ::read(fd, buffer, sizeof(buffer));

if (bytes_in_buffer > 0)

break;

if (bytes_in_buffer == 0)

errno = EIO;

}

case -1:

log("Error: %s:%d - %s", where, errno, std::strerror(errno));

throw std::system_error(errno, std::system_category());

}

}

do {

const inotify_event &event =

*(inotify_event *)((char *)buffer + bytes_handled);

bytes_handled += sizeof(inotify_event) + event.len;

if (bytes_handled >= bytes_in_buffer) {

if (bytes_handled > bytes_in_buffer) {

log("Error: read() - Incomplete event returned");

throw std::system_error(EINVAL, std::system_category());

}

bytes_handled = bytes_in_buffer = 0;

}

const auto it = watches.find(event.wd);

if (it == watches.end()) {

log("Error: read() - Event for unknown wd [%d] possibly due to "

"IN_Q_OVERFLOW",

event.wd);

throw std::system_error(EINVAL, std::system_category());

}

Watch &watch = it->second;

printf("- [%d] %s (%#x)\n", event.wd,

(event.len ? watch.path / event.name : watch.path).c_str(),

event.mask);

if (event.mask & (IN_CREATE | IN_MOVED_TO) && event.mask & IN_ISDIR &&

watch.path.back() != '/') {

const bool in_move = event.mask & IN_MOVED_TO;

const int wd = add_watch(watch.path / event.name, in_move);

if (in_move && wd >= 0 /* != -1 */)

watches.at(wd).in_move = true;

}

if (event.mask & IN_MOVE_SELF) {

if (watch.in_move)

watch.in_move = false;

else if (watch.path.back() == '/')

rm_watch(event.wd);

else

for (const auto &it : watches)

if (it.second.path.compare(0, watch.path.size(), watch.path) == 0)

rm_watch(it.first);

}

if (event.mask & IN_IGNORED) {

printf("[%d] %s deleted\n", event.wd, watch.path.c_str());

watches.erase(event.wd);

}

if (event.mask & mask)

return &event;

} while (bytes_handled > 0);

const int time_elapsed =

std::chrono::duration_cast<std::chrono::milliseconds>(

std::chrono::system_clock::now() - then)

.count();

if (timeout < 0 || (timeout -= time_elapsed) > 0)

return read(timeout, read_delay);

return nullptr;

}

#endif

Соседние файлы в папке СПОВМ_курсач_26