139 lines
3.1 KiB
D
139 lines
3.1 KiB
D
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<ruid>\t<euid>\t<suid>\t<fsuid>"
|
|
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;
|
|
}
|