247 lines
6.3 KiB
D
247 lines
6.3 KiB
D
module dfanotify;
|
||
|
||
public import fanotify;
|
||
|
||
import core.sys.posix.unistd : read, write, close, ssize_t;
|
||
import core.sys.posix.fcntl : O_RDONLY, O_RDWR, O_LARGEFILE, AT_FDCWD;
|
||
import std.exception : enforce;
|
||
import std.string : toStringz, fromStringz;
|
||
import std.conv : to;
|
||
import core.stdc.errno : errno;
|
||
import core.stdc.string : strerror;
|
||
import core.stdc.stdint;
|
||
|
||
// Класс для представления события fanotify (ООП-стиль, с методами для проверки и обработки)
|
||
class FanotifyEvent
|
||
{
|
||
private fanotify_event_metadata meta_;
|
||
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
|
||
{
|
||
return meta_.mask;
|
||
}
|
||
|
||
@property int eventFd() const
|
||
{
|
||
return meta_.fd;
|
||
}
|
||
|
||
@property int pid() const
|
||
{
|
||
return meta_.pid;
|
||
}
|
||
|
||
@property string name() const
|
||
{
|
||
return name_;
|
||
}
|
||
|
||
// Методы проверки событий (без ref)
|
||
bool isOpen() const
|
||
{
|
||
return (mask & FAN_OPEN) != 0;
|
||
}
|
||
|
||
bool isModify() const
|
||
{
|
||
return (mask & FAN_MODIFY) != 0;
|
||
}
|
||
|
||
bool isCloseWrite() const
|
||
{
|
||
return (mask & FAN_CLOSE_WRITE) != 0;
|
||
}
|
||
|
||
bool isCloseNoWrite() const
|
||
{
|
||
return (mask & FAN_CLOSE_NOWRITE) != 0;
|
||
}
|
||
|
||
bool isAccess() const
|
||
{
|
||
return (mask & FAN_ACCESS) != 0;
|
||
}
|
||
|
||
bool isCreate() const
|
||
{
|
||
return (mask & FAN_CREATE) != 0;
|
||
}
|
||
|
||
bool isDelete() const
|
||
{
|
||
return (mask & FAN_DELETE) != 0;
|
||
}
|
||
|
||
bool isOpenPerm() const
|
||
{
|
||
return (mask & FAN_OPEN_PERM) != 0;
|
||
}
|
||
|
||
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()
|
||
{
|
||
if (fd_ >= 0)
|
||
{
|
||
close(fd_);
|
||
fd_ = -1;
|
||
}
|
||
}
|
||
|
||
// Метод для добавления/удаления/модификации меток (управление событиями)
|
||
void mark(uint markFlags, uint64_t eventMask, int dirFd = AT_FDCWD, string path = null)
|
||
{
|
||
const(char)* cPath = path ? path.toStringz() : null;
|
||
int res = fanotify_mark(fd_, markFlags, eventMask, dirFd, cPath);
|
||
enforce(res == 0, "Ошибка маркировки fanotify: " ~ strerror(errno)
|
||
.fromStringz.to!string);
|
||
}
|
||
|
||
// Метод для чтения событий (возвращает массив объектов событий)
|
||
FanotifyEvent[] readEvents(size_t bufferSize = 4096)
|
||
{
|
||
ubyte[] buffer = new ubyte[bufferSize];
|
||
ssize_t len = read(fd_, buffer.ptr, buffer.length);
|
||
if (len <= 0)
|
||
{
|
||
return [];
|
||
}
|
||
|
||
FanotifyEvent[] events;
|
||
size_t offset = 0;
|
||
while (offset + FAN_EVENT_METADATA_LEN <= len)
|
||
{
|
||
auto meta = *(cast(fanotify_event_metadata*)(buffer.ptr + offset));
|
||
if (meta.event_len < FAN_EVENT_METADATA_LEN || offset + meta.event_len > len)
|
||
{
|
||
break;
|
||
}
|
||
|
||
string name;
|
||
size_t infoOffset = offset + fanotify_event_metadata.sizeof;
|
||
while (infoOffset < offset + meta.event_len)
|
||
{
|
||
auto hdr = *(cast(fanotify_event_info_header*)(buffer.ptr + infoOffset));
|
||
if (hdr.len == 0 || infoOffset + hdr.len > offset + meta.event_len)
|
||
{
|
||
break;
|
||
}
|
||
|
||
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;
|
||
auto handle = *(cast(file_handle*)(buffer.ptr + fidOffset));
|
||
size_t handleEnd = fidOffset + file_handle.sizeof + handle.handle_bytes;
|
||
if (handleEnd < offset + meta.event_len)
|
||
{
|
||
name = (cast(char*)(buffer.ptr + handleEnd)).fromStringz.to!string;
|
||
}
|
||
}
|
||
infoOffset += hdr.len;
|
||
}
|
||
|
||
auto ev = new FanotifyEvent(meta, name, fd_);
|
||
events ~= ev;
|
||
offset += meta.event_len;
|
||
}
|
||
return events;
|
||
}
|
||
|
||
// Метод для постобработки всех событий (вызывает postProcess на каждом)
|
||
void postProcessEvents(FanotifyEvent[] events)
|
||
{
|
||
foreach (ev; events)
|
||
{
|
||
ev.postProcess();
|
||
}
|
||
}
|
||
|
||
// Геттер для fd (если нужно внешне, но лучше использовать методы)
|
||
@property int handle() const
|
||
{
|
||
return fd_;
|
||
}
|
||
}
|