dwatch/source/app.d

234 lines
7.3 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.

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, O_CLOEXEC;
import std.exception : enforce, collectException;
import std.string : toStringz, fromStringz;
import std.conv : to;
import core.stdc.errno : errno, EINTR;
import core.stdc.string : strerror;
import core.stdc.stdint;
import std.stdio : writeln;
import std.format : format;
import std.file : readLink, FileException;
string readlinkFdPath(int fd)
{
string link = "/proc/self/fd/%d".format(fd);
string path;
// Заглушка для FileException
collectException!FileException(path = link.readLink());
return path;
}
struct FanotifyMetadataRange {
private fanotify_event_metadata* current; // Current position in buffer.
private size_t remainingLen; // Remaining bytes in buffer from current.
// Private helper functions (now non-static const methods inside the class):
private @nogc nothrow @trusted
bool FAN_EVENT_OK(const(fanotify_event_metadata)* meta, size_t len) const {
enum long BASE = cast(long) FAN_EVENT_METADATA_LEN;
const long L = cast(long) len;
const long EL = cast(long) meta.event_len;
return (L >= BASE) && (EL >= BASE) && (EL <= L);
}
private @nogc nothrow @trusted
fanotify_event_metadata* FAN_EVENT_NEXT(fanotify_event_metadata* meta, ref size_t len) const {
const uint step = meta.event_len;
len -= step;
return cast(fanotify_event_metadata*)((cast(ubyte*) meta) + step);
}
// Constructor: Takes the buffer slice and read length.
this(ubyte[] buffer, size_t len) @nogc nothrow @trusted {
if (len < FAN_EVENT_METADATA_LEN) {
remainingLen = 0; // Empty if too small.
return;
}
current = cast(fanotify_event_metadata*) buffer.ptr;
remainingLen = len;
}
// Range primitives:
@nogc nothrow @trusted
bool empty() const {
return !FAN_EVENT_OK(current, remainingLen);
}
@nogc nothrow @trusted
ref const(fanotify_event_metadata) front() const {
assert(!empty, "Range is empty");
return *current; // Returns by const ref to avoid copies.
}
@nogc nothrow @trusted
void popFront() {
assert(!empty, "Range is empty");
current = FAN_EVENT_NEXT(current, remainingLen);
}
}
// Класс для представления события fanotify (ООП-стиль, с методами для проверки и обработки)
struct FanotifyEvent
{
private fanotify_event_metadata _meta;
private int _fanFd;
// Деструктор: автоматически закрывает fd события, если он валиден (RAII)
// ~this()
// {
// if (_meta.fd >= 0 && _meta.fd != FAN_NOFD)
// {
// close(_meta.fd);
// }
// }
// Геттеры (value types)
@property uint64_t mask() const { return _meta.mask; }
@property int eventFd() const { return _meta.fd; }
@property int pid() const { return _meta.pid; }
// Методы проверки событий
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; }
// Метод для отправки 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)
{
size_t sz = bufferSize;
if (sz < FAN_EVENT_METADATA_LEN) sz = FAN_EVENT_METADATA_LEN;
ubyte[] buffer = new ubyte[sz];
ssize_t len;
while (true)
{
len = read(_fd, buffer.ptr, buffer.length);
if (len < 0 && errno == EINTR) continue;
break;
}
if (len <= 0) return [];
auto range = FanotifyMetadataRange(buffer, cast(size_t) len);
import std.array : Appender;
auto events = Appender!(FanotifyEvent[])();
// FanotifyEvent[] events;
foreach (ref const meta; range) {
events ~= FanotifyEvent(meta, _fd);
}
return events.data;
}
// Геттер для fd (если нужно внешне, но лучше использовать методы)
@property int handle() const
{
return _fd;
}
}
ulong dirMask()
{
return FAN_EVENT_ON_CHILD |
FAN_OPEN_PERM | FAN_OPEN_EXEC_PERM | FAN_ACCESS_PERM |
FAN_OPEN | FAN_CLOSE_WRITE;
}
void main(string[] args)
{
enum initFlags = FAN_CLASS_PRE_CONTENT | FAN_CLOEXEC | FAN_NONBLOCK;
auto fan = new Fanotify(initFlags, O_RDONLY | O_LARGEFILE | O_CLOEXEC);
// fan.mark(FAN_MARK_ADD | FAN_MARK_FILESYSTEM, dirMask(), AT_FDCWD, "/home");
fan.mark(FAN_MARK_ADD | FAN_MARK_FILESYSTEM, dirMask(), AT_FDCWD, "/tmp");
for (;;)
{
auto list = fan.readEvents(8_192);
foreach (fev; list)
{
if (fev.isOverflow)
{
return;
}
if (fev.isOpenPerm || fev.isAccessPerm || fev.isOpenExecPerm)
{
writeln(fev.eventFd.readlinkFdPath);
fev.respond(FAN_ALLOW);
continue;
}
if (fev.isCloseWrite)
{
continue;
}
}
}
}