dwatch/source/dfanotify.d

252 lines
6.4 KiB
D
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 isMoved() const
{
return (mask & FAN_MOVED_FROM) != 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_;
}
}