252 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			D
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
	
		
			6.4 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 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_;
 | 
						||
	}
 | 
						||
}
 |