Промежуток 1
This commit is contained in:
parent
ed0d2780c3
commit
cb3f40deee
2 changed files with 171 additions and 57 deletions
65
source/app.d
65
source/app.d
|
@ -1,56 +1,77 @@
|
||||||
import dfanotify;
|
import dfanotify;
|
||||||
|
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
import std.file : readLink;
|
|
||||||
import std.format : format;
|
import std.format : format;
|
||||||
import core.sys.posix.fcntl : AT_FDCWD;
|
import core.sys.posix.fcntl : AT_FDCWD;
|
||||||
import core.sys.posix.unistd : close;
|
|
||||||
import core.stdc.stdlib : exit;
|
import core.stdc.stdlib : exit;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
Fanotify fan;
|
Fanotify fan;
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
fan = new Fanotify(
|
fan = new Fanotify(
|
||||||
FAN_CLASS_NOTIF | FAN_CLOEXEC | FAN_REPORT_FID | FAN_REPORT_DIR_FID | FAN_REPORT_NAME);
|
FAN_CLASS_NOTIF | FAN_CLOEXEC | FAN_REPORT_FID | FAN_REPORT_DIR_FID | FAN_REPORT_NAME
|
||||||
} catch (Exception e) {
|
);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
writeln(e.msg);
|
writeln(e.msg);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto eventMask = FAN_OPEN | FAN_MODIFY | FAN_CLOSE | FAN_CREATE | FAN_DELETE | FAN_EVENT_ON_CHILD;
|
auto eventMask = FAN_OPEN | FAN_MODIFY | FAN_CLOSE | FAN_CREATE | FAN_DELETE | FAN_EVENT_ON_CHILD | FAN_ACCESS;
|
||||||
|
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
fan.mark(FAN_MARK_ADD | FAN_MARK_ONLYDIR, eventMask, AT_FDCWD, "/tmp/scripts");
|
fan.mark(FAN_MARK_ADD | FAN_MARK_ONLYDIR, eventMask, AT_FDCWD, "/tmp/scripts");
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
writeln(e.msg);
|
writeln(e.msg);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln("Мониторинг запущен для /tmp/scripts...");
|
writeln(
|
||||||
|
"Мониторинг с разрешениями запущен для /tmp/scripts...");
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
auto events = fan.readEvents();
|
auto events = fan.readEvents();
|
||||||
|
|
||||||
foreach (ref e; events)
|
foreach (e; events)
|
||||||
{
|
{
|
||||||
string path = e.name.length ? e.name : "unknown";
|
string path = e.name.length ? e.name : "unknown";
|
||||||
|
|
||||||
writefln("Событие: mask=0x%x, pid=%d, name/path=%s", e.mask, e.pid, path);
|
writefln("Событие: mask=0x%x, pid=%d, name/path=%s", e.mask, e.pid, path);
|
||||||
|
|
||||||
if (e.isOpen)
|
// Обработка permission-события
|
||||||
writeln(" - Открытие файла");
|
if (e.isOpenPerm)
|
||||||
if (e.isModify)
|
{
|
||||||
writeln(" - Модификация файла");
|
writeln(" - Запрос на открытие файла. Отклоняем (предполагаем попытку записи).");
|
||||||
if (e.isCloseWrite)
|
e.respond(FAN_DENY); // Отклонить (для FAN_ALLOW используйте FAN_ALLOW)
|
||||||
writeln(" - Закрытие после записи");
|
}
|
||||||
if (e.isCloseNoWrite)
|
else
|
||||||
writeln(" - Закрытие без записи");
|
{
|
||||||
if (e.isCreate)
|
// Обычные уведомления (не permission)
|
||||||
writeln(" - Создание файла/директории");
|
if (e.isOpen)
|
||||||
if (e.isDelete)
|
writeln(" - Открытие файла");
|
||||||
writeln(" - Удаление файла/директории");
|
if (e.isAccess)
|
||||||
|
writeln(" - Доступ к файлу");
|
||||||
|
if (e.isModify)
|
||||||
|
writeln(" - Модификация файла");
|
||||||
|
if (e.isCloseWrite)
|
||||||
|
writeln(" - Закрытие после записи");
|
||||||
|
if (e.isCloseNoWrite)
|
||||||
|
writeln(" - Закрытие без записи");
|
||||||
|
if (e.isCreate)
|
||||||
|
writeln(" - Создание файла/директории");
|
||||||
|
if (e.isDelete)
|
||||||
|
writeln(" - Удаление файла/директории");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Опционально: вызов постобработки
|
||||||
|
e.postProcess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ module dfanotify;
|
||||||
|
|
||||||
public import fanotify;
|
public import fanotify;
|
||||||
|
|
||||||
import core.sys.posix.unistd : read, close, ssize_t;
|
import core.sys.posix.unistd : read, write, close, ssize_t;
|
||||||
import core.sys.posix.fcntl : O_RDONLY, O_RDWR, O_LARGEFILE, AT_FDCWD;
|
import core.sys.posix.fcntl : O_RDONLY, O_RDWR, O_LARGEFILE, AT_FDCWD;
|
||||||
import std.exception : enforce;
|
import std.exception : enforce;
|
||||||
import std.string : toStringz, fromStringz;
|
import std.string : toStringz, fromStringz;
|
||||||
|
@ -11,26 +11,53 @@ import core.stdc.errno : errno;
|
||||||
import core.stdc.string : strerror;
|
import core.stdc.string : strerror;
|
||||||
import core.stdc.stdint;
|
import core.stdc.stdint;
|
||||||
|
|
||||||
struct FanotifyEvent
|
// Класс для представления события fanotify (ООП-стиль, с методами для проверки и обработки)
|
||||||
|
class FanotifyEvent
|
||||||
{
|
{
|
||||||
fanotify_event_metadata meta;
|
private fanotify_event_metadata meta_;
|
||||||
string name;
|
private string name_;
|
||||||
|
private int fanFd_; // Ссылка на fanotify fd для отправки response (копируется при создании)
|
||||||
|
|
||||||
|
// Конструктор (value semantics, копирует данные)
|
||||||
|
this(fanotify_event_metadata meta, string name, int fanFd)
|
||||||
|
{
|
||||||
|
meta_ = meta;
|
||||||
|
name_ = name;
|
||||||
|
fanFd_ = fanFd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Деструктор: автоматически закрывает fd события, если он валиден (RAII)
|
||||||
|
~this()
|
||||||
|
{
|
||||||
|
if (meta_.fd >= 0 && meta_.fd != FAN_NOFD)
|
||||||
|
{
|
||||||
|
close(meta_.fd);
|
||||||
|
meta_.fd = -1; // Избегаем повторного закрытия
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Геттеры (value types)
|
||||||
@property uint64_t mask() const
|
@property uint64_t mask() const
|
||||||
{
|
{
|
||||||
return meta.mask;
|
return meta_.mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property int eventFd() const
|
@property int eventFd() const
|
||||||
{
|
{
|
||||||
return meta.fd;
|
return meta_.fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property int pid() const
|
@property int pid() const
|
||||||
{
|
{
|
||||||
return meta.pid;
|
return meta_.pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property string name() const
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Методы проверки событий (без ref)
|
||||||
bool isOpen() const
|
bool isOpen() const
|
||||||
{
|
{
|
||||||
return (mask & FAN_OPEN) != 0;
|
return (mask & FAN_OPEN) != 0;
|
||||||
|
@ -65,44 +92,97 @@ struct FanotifyEvent
|
||||||
{
|
{
|
||||||
return (mask & FAN_DELETE) != 0;
|
return (mask & FAN_DELETE) != 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class Fanotify
|
bool isOpenPerm() const
|
||||||
{
|
|
||||||
private int fd = -1;
|
|
||||||
|
|
||||||
this(uint initFlags, uint eventFFlags = O_RDONLY | O_LARGEFILE)
|
|
||||||
{
|
{
|
||||||
fd = fanotify_init(initFlags, eventFFlags);
|
return (mask & FAN_OPEN_PERM) != 0;
|
||||||
enforce(fd >= 0, "Ошибка инициализации fanotify: " ~ to!string(fd));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isAccessPerm() const
|
||||||
|
{
|
||||||
|
return (mask & FAN_ACCESS_PERM) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOpenExecPerm() const
|
||||||
|
{
|
||||||
|
return (mask & FAN_OPEN_EXEC_PERM) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOverflow() const
|
||||||
|
{
|
||||||
|
return (mask & FAN_Q_OVERFLOW) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFsError() const
|
||||||
|
{
|
||||||
|
return (mask & FAN_FS_ERROR) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Метод для постобработки события (виртуальный, можно override для кастомной логики)
|
||||||
|
void postProcess()
|
||||||
|
{
|
||||||
|
// По умолчанию ничего, но можно добавить логику, например, логирование
|
||||||
|
}
|
||||||
|
|
||||||
|
// Метод для отправки response (для permission-событий), закрывает fd автоматически после
|
||||||
|
void respond(uint response)
|
||||||
|
{
|
||||||
|
if (eventFd < 0 || eventFd == FAN_NOFD)
|
||||||
|
{
|
||||||
|
return; // Нет fd для response
|
||||||
|
}
|
||||||
|
|
||||||
|
fanotify_response resp;
|
||||||
|
resp.fd = eventFd;
|
||||||
|
resp.response = response;
|
||||||
|
|
||||||
|
ssize_t res = write(fanFd_, &resp, fanotify_response.sizeof);
|
||||||
|
enforce(res == fanotify_response.sizeof, "Ошибка записи response: " ~ strerror(errno)
|
||||||
|
.fromStringz.to!string);
|
||||||
|
|
||||||
|
// Закрываем fd сразу после response (не ждем деструктора, но деструктор на всякий случай)
|
||||||
|
close(meta_.fd);
|
||||||
|
meta_.fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Основной ООП-класс для управления fanotify
|
||||||
|
class Fanotify
|
||||||
|
{
|
||||||
|
private int fd_ = -1;
|
||||||
|
|
||||||
|
// Конструктор: инициализация с флагами
|
||||||
|
this(uint initFlags, uint eventFFlags = O_RDONLY | O_LARGEFILE)
|
||||||
|
{
|
||||||
|
fd_ = fanotify_init(initFlags, eventFFlags);
|
||||||
|
enforce(fd_ >= 0, "Ошибка инициализации fanotify: " ~ strerror(errno)
|
||||||
|
.fromStringz.to!string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Деструктор: автоматически закрывает fanotify fd
|
||||||
~this()
|
~this()
|
||||||
{
|
{
|
||||||
if (fd >= 0)
|
if (fd_ >= 0)
|
||||||
{
|
{
|
||||||
close(fd);
|
close(fd_);
|
||||||
fd = -1;
|
fd_ = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Метод для добавления/удаления/модификации меток (управление событиями)
|
||||||
void mark(uint markFlags, uint64_t eventMask, int dirFd = AT_FDCWD, string path = null)
|
void mark(uint markFlags, uint64_t eventMask, int dirFd = AT_FDCWD, string path = null)
|
||||||
{
|
{
|
||||||
const(char)* cPath = path ? path.toStringz() : null;
|
const(char)* cPath = path ? path.toStringz() : null;
|
||||||
int res = fanotify_mark(fd, markFlags, eventMask, dirFd, cPath);
|
int res = fanotify_mark(fd_, markFlags, eventMask, dirFd, cPath);
|
||||||
if (res == -1)
|
enforce(res == 0, "Ошибка маркировки fanotify: " ~ strerror(errno)
|
||||||
{
|
.fromStringz.to!string);
|
||||||
string errMsg = "Ошибка маркировки fanotify: " ~ to!string(
|
|
||||||
res) ~ " (errno: " ~ to!string(errno) ~ ", " ~ strerror(errno)
|
|
||||||
.fromStringz.to!string ~ ")";
|
|
||||||
throw new Exception(errMsg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Метод для чтения событий (возвращает массив объектов событий)
|
||||||
FanotifyEvent[] readEvents(size_t bufferSize = 4096)
|
FanotifyEvent[] readEvents(size_t bufferSize = 4096)
|
||||||
{
|
{
|
||||||
ubyte[] buffer = new ubyte[bufferSize];
|
ubyte[] buffer = new ubyte[bufferSize];
|
||||||
ssize_t len = read(fd, buffer.ptr, buffer.length);
|
ssize_t len = read(fd_, buffer.ptr, buffer.length);
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
|
@ -112,43 +192,56 @@ class Fanotify
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
while (offset + FAN_EVENT_METADATA_LEN <= len)
|
while (offset + FAN_EVENT_METADATA_LEN <= len)
|
||||||
{
|
{
|
||||||
auto meta = cast(fanotify_event_metadata*)(buffer.ptr + offset);
|
auto meta = *(cast(fanotify_event_metadata*)(buffer.ptr + offset));
|
||||||
if (meta.event_len < FAN_EVENT_METADATA_LEN || offset + meta.event_len > len)
|
if (meta.event_len < FAN_EVENT_METADATA_LEN || offset + meta.event_len > len)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
FanotifyEvent ev = FanotifyEvent(*meta);
|
string name;
|
||||||
|
|
||||||
size_t infoOffset = offset + fanotify_event_metadata.sizeof;
|
size_t infoOffset = offset + fanotify_event_metadata.sizeof;
|
||||||
while (infoOffset < offset + meta.event_len)
|
while (infoOffset < offset + meta.event_len)
|
||||||
{
|
{
|
||||||
auto hdr = cast(fanotify_event_info_header*)(buffer.ptr + infoOffset);
|
auto hdr = *(cast(fanotify_event_info_header*)(buffer.ptr + infoOffset));
|
||||||
if (hdr.len == 0 || infoOffset + hdr.len > offset + meta.event_len)
|
if (hdr.len == 0 || infoOffset + hdr.len > offset + meta.event_len)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME)
|
if (hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME ||
|
||||||
|
hdr.info_type == FAN_EVENT_INFO_TYPE_OLD_DFID_NAME ||
|
||||||
|
hdr.info_type == FAN_EVENT_INFO_TYPE_NEW_DFID_NAME)
|
||||||
{
|
{
|
||||||
size_t fidOffset = infoOffset + fanotify_event_info_header.sizeof + __kernel_fsid_t.sizeof;
|
size_t fidOffset = infoOffset + fanotify_event_info_header.sizeof + __kernel_fsid_t.sizeof;
|
||||||
auto handle = cast(file_handle*)(buffer.ptr + fidOffset);
|
auto handle = *(cast(file_handle*)(buffer.ptr + fidOffset));
|
||||||
size_t handleEnd = fidOffset + file_handle.sizeof + handle.handle_bytes;
|
size_t handleEnd = fidOffset + file_handle.sizeof + handle.handle_bytes;
|
||||||
if (handleEnd < offset + meta.event_len)
|
if (handleEnd < offset + meta.event_len)
|
||||||
{
|
{
|
||||||
ev.name = (cast(char*)(buffer.ptr + handleEnd)).fromStringz.to!string;
|
name = (cast(char*)(buffer.ptr + handleEnd)).fromStringz.to!string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
infoOffset += hdr.len;
|
infoOffset += hdr.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ev = new FanotifyEvent(meta, name, fd_);
|
||||||
events ~= ev;
|
events ~= ev;
|
||||||
offset += meta.event_len;
|
offset += meta.event_len;
|
||||||
}
|
}
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Метод для постобработки всех событий (вызывает postProcess на каждом)
|
||||||
|
void postProcessEvents(FanotifyEvent[] events)
|
||||||
|
{
|
||||||
|
foreach (ev; events)
|
||||||
|
{
|
||||||
|
ev.postProcess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Геттер для fd (если нужно внешне, но лучше использовать методы)
|
||||||
@property int handle() const
|
@property int handle() const
|
||||||
{
|
{
|
||||||
return fd;
|
return fd_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue