From 7f54a56ef548a9e1ffa16fec2d8904809485db9d Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Mon, 1 Sep 2025 18:10:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D1=87=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B7=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5?= =?UTF-8?q?=D0=BA=D1=83=20sdiff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dub.json | 9 +++++++- source/app.d | 62 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/dub.json b/dub.json index 60ff96a..a636f59 100644 --- a/dub.json +++ b/dub.json @@ -8,7 +8,14 @@ "name": "dwatch", "targetPath": "bin", "targetType": "executable", + "libs": [ + "xdiff" + ], "dependencies": { - "fanotify": "~>0.1.0" + "fanotify": "~>0.1.0", + "sdiff": { + "repository": "git+git+https://git.zhirov.kz/dlang/sdiff.git", + "version": "8c388b112360a9f5bf89b9aa12b1977b8967b219" + } } } \ No newline at end of file diff --git a/source/app.d b/source/app.d index 0d80ce7..ccc4fe2 100644 --- a/source/app.d +++ b/source/app.d @@ -10,7 +10,7 @@ import core.sys.posix.sys.stat : fstat, stat_t; import core.stdc.errno : errno, EINTR; import core.stdc.string : strerror; import std.stdio : writeln, writefln, stderr; -import std.file : isDir, readText, exists; +import std.file : isDir, readText, exists, read; import std.string : strip, splitLines, startsWith, toStringz, fromStringz, split; import std.conv : to; import std.exception : enforce; @@ -147,6 +147,8 @@ struct Session uint uid; // LOGINUID из /proc/PID/loginuid string filePathOpen; // путь, снятый на PERM bool changed; // был CLOSE_WRITE + const(ubyte)[] fileBytesSource; + const(ubyte)[] fileBytesDiff; } __gshared Session[DevIno] gMap; // (pid,dev,ino) -> Session @@ -184,12 +186,28 @@ ulong dirMask() FAN_OPEN | FAN_CLOSE_WRITE; } +ubyte[] readAllFromFd(int fd) +{ + import std.array : appender; + + ubyte[16_384] buf; // 16 KiB буфер + auto output = appender!(ubyte[]); + for (;;) + { + auto n = read(fd, buf.ptr, buf.length); + if (n <= 0) + break; + output.put(buf[0 .. n]); + } + return output.data; +} + /// ---- обработчики ---- void onOpenPerm(FanotifyEvent ev, Epoll ep) { const pid = ev.pid; auto procName = readProcComm(pid); - auto ids = readProcIds(pid); // <-- ruid + loginuid + auto ids = readProcIds(pid); DevIno key; if (!getDevIno(ev.eventFd, key, pid)) @@ -200,13 +218,26 @@ void onOpenPerm(FanotifyEvent ev, Epoll ep) auto path = readlinkFdPath(ev.eventFd); + const(ubyte)[] content; + try + { + if (path.length) + { + content = readAllFromFd(ev.eventFd); + } + } + catch (Exception e) + { + content = []; + } + if (auto ps = key in gMap) { // уже есть } else { - gMap[key] = Session(procName, ids.ruid, ids.uid, path, false); + gMap[key] = Session(procName, ids.ruid, ids.uid, path, false, content); } if (!(pid in gPidfdByPid)) @@ -214,7 +245,7 @@ void onOpenPerm(FanotifyEvent ev, Epoll ep) int pfd = -1; try { - pfd = pidfd_open(pid, 0); // на твоей системе так ок + pfd = pidfd_open(pid, 0); if (pfd < 0) writeln("pidfd_open failed: ", strerror(errno).fromStringz); } @@ -244,7 +275,27 @@ void onCloseWrite(FanotifyEvent ev) if (!getDevIno(ev.eventFd, key, ev.pid)) return; if (auto ps = key in gMap) + { ps.changed = true; + + import sdiff; + + const(ubyte)[] content; + try + { + if (ps.filePathOpen.length) + content = readAllFromFd(ev.eventFd); + } + catch (Exception e) + { + content = []; + } + + auto dataSource = new MMFile(ps.fileBytesSource); + auto dataChange = new MMFile(content); + + ps.fileBytesDiff = dataSource.diff(dataChange).slice(); + } } void flushAndClearForPid(int pid, Epoll ep) @@ -256,7 +307,10 @@ void flushAndClearForPid(int pid, Epoll ep) continue; auto s = gMap[k]; if (s.changed) + { logChange(s.procName, s.filePathOpen, s.uid, s.ruid); + writeln(cast(string)s.fileBytesDiff); + } gMap.remove(k); } if (auto p = pid in gPidfdByPid)