module event_actor; import core.sys.posix.pwd : passwd, getpwuid_r; import core.sys.posix.sys.types : uid_t; import core.stdc.stdlib : malloc, free; import core.stdc.string : strlen; import std.file : readText, exists; import std.string : splitLines, strip; import std.conv : to; // для токенизации по пробельным import std.ascii : isWhite; import std.algorithm.iteration : splitter; import std.array : array; struct EventActor { uid_t uid; // выбранный UID (loginuid если есть, иначе ruid) string user; // имя пользователя для uid (может быть пустым) uid_t ruid, euid, suid, fsuid; string ruser, euser, suser, fsuser; bool hasLoginuid; uid_t loginuid; string loginuser; } private string userName(uid_t uid) { enum BUF = 4096; auto buf = cast(char*) malloc(BUF); scope (exit) if (buf) free(buf); passwd pwd; passwd* outp; auto rc = getpwuid_r(uid, &pwd, buf, BUF, &outp); if (rc == 0 && outp !is null) { return pwd.pw_name[0 .. strlen(pwd.pw_name)].idup; } return ""; } /// Парсит строку вида: "Uid:\t\t\t\t" private bool parseUidLine(string line, out uid_t r, out uid_t e, out uid_t s, out uid_t f) { // разобьём по любым пробельным символам auto toks = line.splitter!isWhite.array; // ["Uid:", "1000", "1000", "1000", "1000"] if (toks.length >= 5 && toks[0] == "Uid:") { try { r = toks[1].to!uid_t; e = toks[2].to!uid_t; s = toks[3].to!uid_t; f = toks[4].to!uid_t; return true; } catch (Exception e) { } } return false; } /// Главная функция: кто инициировал событие (по PID из fanotify_event_metadata) EventActor eventActorFromPid(int pid) { EventActor a; if (pid <= 0) return a; const base = "/proc/" ~ pid.to!string ~ "/"; const statusPath = base ~ "status"; if (exists(statusPath)) { try { auto txt = readText(statusPath); uid_t r = 0, e = 0, s = 0, f = 0; foreach (line; txt.splitLines()) { // ищем строку "Uid:" if (line.length >= 4 && line[0 .. 4] == "Uid:") { if (parseUidLine(line, r, e, s, f)) { a.ruid = r; a.euid = e; a.suid = s; a.fsuid = f; a.ruser = userName(r); a.euser = userName(e); a.suser = userName(s); a.fsuser = userName(f); } break; } } } catch (Exception e) { } } // loginuid — владелец сессии (если audit включён) const loginPath = base ~ "loginuid"; if (exists(loginPath)) { try { auto s = readText(loginPath).strip; if (s.length) { a.loginuid = s.to!uid_t; a.loginuser = userName(a.loginuid); a.hasLoginuid = true; } } catch (Exception e) { } } // выбираем «итогового» пользователя if (a.hasLoginuid) { a.uid = a.loginuid; a.user = a.loginuser.length ? a.loginuser : ("uid:" ~ a.loginuid.to!string); } else { a.uid = a.ruid; a.user = a.ruser.length ? a.ruser : ("uid:" ~ a.ruid.to!string); } return a; }