As games.log is always the largest file, I wanted to keep that in a separate script. This script handles the rest.
Yet again, having all of this parsed, gives us access to do whatever sort of automatisation we want. For example, we have a daily post about the last day abusers and the admins who did it.
Table spec:
Script yet again has room for improvement and can be unreadable at parts, but it works fine.
As with games.log, the files are moved to the PHP script folder (file /root/copylogs).
And the parser script itself:
Yet again, having all of this parsed, gives us access to do whatever sort of automatisation we want. For example, we have a daily post about the last day abusers and the admins who did it.
Table spec:
SQL:
CREATE TABLE abuse_adminlog (
abuse_adminlog_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY
, dt DATETIME
, action VARCHAR(255)
, byip VARCHAR(255)
, byname VARCHAR(255)
, toip VARCHAR(255)
, toname VARCHAR(255)
);
CREATE TABLE abuse_loginlog (
login_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY
, dt DATETIME
, player VARCHAR(64)
, adminLevel TINYINT
, ip VARCHAR(20)
);
CREATE TABLE abuse_rconlog (
abuse_rconlog_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY
, dt DATETIME
, action VARCHAR(255)
, ip VARCHAR(255)
);
CREATE INDEX idx_adminlog_dt ON abuse_gameslog(dt);
CREATE INDEX idx_loginlog_dt ON abuse_loginlog(dt);
CREATE INDEX idx_rconlog_dt ON abuse_rconlog(dt);
Script yet again has room for improvement and can be unreadable at parts, but it works fine.
As with games.log, the files are moved to the PHP script folder (file /root/copylogs).
Bash:
#!/bin/bash
srvPath="/home/server/"
logFolder="1fx/logs/"
logFolderRcon="1fx/logs/"
adminLogName="admin.log"
rconLogName="rcon.log"
loginLogName="login.log"
destination="/var/www/xxxx/"
cp $srvPath$logFolder$adminLogName $destination
echo -n > $srvPath$logFolder$adminLogName
cp $srvPath$logFolderRcon$rconLogName $destination
echo -n > $srvPath$logFolderRcon$rconLogName
cp $srvPath$logFolder$loginLogName $destination
echo -n > $srvPath$logFolder$loginLogName
chown www-data $destination$adminLogName
chown www-data $destination$rconLogName
chown www-data $destination$loginLogName
And the parser script itself:
PHP:
<?php
$handle = ssh2_connect("127.0.0.1", 22);
if (!ssh2_auth_password($handle, "username", "password")) {
throw new Exception("Can't connect!");
}
$stream = ssh2_exec($handle, "/root/copylogs");
stream_set_blocking($stream, true);
stream_set_timeout($stream, 2);
$stream_err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
print_r(array("stdio" => stream_get_contents($stream), "stderr" => stream_get_contents($stream_err)));
require_once __DIR__ . "/classes/log/RCON.php";
require_once __DIR__ . "/classes/log/Admin.php";
require_once __DIR__ . "/classes/log/LoginLog.php";
require_once __DIR__ . "/classes/pdo/SQL.php";
$arr = file_get_contents(__DIR__ . "/admin.log");
$array = explode("\n", $arr);
//parse the array, swap shit
//print_r($array);
$objects = array();
foreach ($array as $line) {
$split = preg_split("/[\s\\\\]+/", $line);
//as this shit isn't as easy as rcon, we gotta read contents.
if (sizeof($split) < 4) {
continue;
}
//0-1 is the datetime object.
$dt = new DateTime($split[0] . " " . $split[1]);
//read action up until we hit "by" followed with an IP.
$action = "";
$i = 0;
$byip = "";
$byadm = 5;
$by = "";
for ($i = 2; $i < sizeof($split); $i++) {
if (trim(substr(trim($split[$i]), strlen(trim($split[$i])) - 2, strlen(trim($split[$i])))) === "by" && (filter_var(trim($split[$i + 1]), FILTER_VALIDATE_IP) || trim($split[$i + 1]) === "RCON")) {
$action .= substr(trim($split[$i]), 0, strlen(trim($split[$i])) - 2);
$i++;
$byip = trim($split[$i]);
if ($byip === "RCON") {
break;
}
//$i++;
//$byadm = trim($split[$i]); this is supposed to be the admin level of the person, but that is not available on public 1fx release
$i++;
break;
}
if (trim($split[$i]) === "friendlyfire") {
//friendlyfire isn't quite that *friendly*.
$action = trim($split[$i]) . " ";
$i++;
$action .= substr(trim($split[$i]), 0, strlen(trim($split[$i])) - 2);
$i++;
$byip = trim($split[$i]);
if ($byip === "RCON") {
break;
}
//$i++;
//$byadm = trim($split[$i]); this is supposed to be the admin level of the person, but that is not available on public 1fx release
$i++;
break;
}
if (trim($split[$i]) === "by" && (filter_var(trim($split[$i + 1]), FILTER_VALIDATE_IP) || trim($split[$i + 1]) === "RCON")) {
$i++;
$byip = trim($split[$i]);
if ($byip === "RCON") {
break;
}
//$i++;
//$byadm = trim($split[$i]); this is supposed to be the admin level of the person, but that is not available on public 1fx release
$i++;
break; //got the action.
}
$action .= $split[$i] . " ";
}
$action = trim($action);
if ($action === "altmap switch") {
$action .= " " . $split[sizeof($split) - 3] . " " . $split[sizeof($split) - 2] . " " . $split[sizeof($split) - 1];
for ($i; $i < sizeof($split) - 3; $i++) {
$by .= $split[$i] . " ";
}
$by = trim($by);
$toip = "";
$to = "";
$action = htmlentities($action);
$by = htmlentities($by);
$objects[] = new Admin($dt, $action, $byip, $by, $toip, $to, $byadm);
continue;
}
//we got action, now to by.
$toip = null;
for ($i; $i < sizeof($split); $i++) {
if (trim($split[$i]) === "to" && (filter_var(trim($split[$i + 1]), FILTER_VALIDATE_IP) || (trim($split[$i + 1]) === "all" && trim($split[$i + 2]) === "clients" ) || (trim($split[$i + 1]) === "AllPlayer"))) { //this case will not be reached if sizeof is too small, e.g. action not concerning a to
$i++;
$toip = trim($split[$i]);
$i++;
break; //got the action.
}
$by .= $split[$i] . " ";
}
$by = trim($by);
$to = null;
for ($i; $i < sizeof($split); $i++) {
$to .= $split[$i] . " ";
}
$to = trim($to);
if (($toip === null || strlen(trim($toip)) === 0) && ($to === null || strlen($to) === 0)) {
if (trim($action) === "remove admin") {
$nspl = preg_split("/[\s\\\\]+/", $by);
$gotStr = false;
$by = "";
for ($j = 0; $j < sizeof($nspl); $j++) {
$str = trim($nspl[$j]);
if ($str === "to") {
$gotStr = true;
continue;
}
if ($gotStr) {
$action .= " " . $str;
} else {
$by .= " " . $str;
}
}
$action = trim($action);
$by = trim($by);
}
}
$objects[] = new Admin($dt, $action, $byip, $by, $toip, $to, $byadm);
}
$dsn = "mysql:host=localhost;dbname=database";
$username = "username";
$password = "password";
$sql = new SQL($dsn, $username, $password);
//as there can be shitton of rows, use transaction so if we fail, we fail with everything and don't have to figure out where we failed.
$sql->beginTransaction();
foreach ($objects as $object) {
$sql->query("INSERT INTO abuse_adminlog (dt, action, byip, byname, toip, toname, adminlevel) VALUES (?, ?, ?, ?, ?, ?, ?)", $object->toSQLParams());
}
$sql->commit();
$objects = array();
$arr = file_get_contents(__DIR__ . "/rcon.log");
$array = explode("\n", $arr);
foreach ($array as $line) {
$action = $ip = $dt = null;
$split = preg_split("/[\s]+/", $line);
if (sizeof($split) < 4) {
continue; //probably empty line, weird shit.
}
//split the line so we can parse the shit out of it.
$ip = trim(explode(":", $split[sizeof($split) - 1])[0]);
for ($i = 2;$i < sizeof($split) - 2; $i++) {
$action .= $split[$i] . " ";
}
$action = strtolower(trim($action));
if ($ip === "127.0.0.1" || $ip === gethostbyname(gethostname()) || $ip === "https://xxxx.3d-sof2.com") {
if ($action === "g_enablehash" || $action === "status") {
continue;
}
$ip = "ServerPanel Web RCON";
}
//read up until len of array - 1, that should be "by"
$dt = new DateTime($split[0] . " " . $split[1]);
//2nd is action, read until sizeof array -2
$objects[] = new RCON($dt, $action, $ip);
}
$stream = ssh2_exec($handle, "echo -n > /var/www/xxxx/rcon.log");
stream_set_blocking($stream, true);
stream_set_timeout($stream, 2);
$stream_err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
print_r(array("stdio" => stream_get_contents($stream), "stderr" => stream_get_contents($stream_err)));
$stream = ssh2_exec($handle, "echo -n > /var/www/xxxx/admin.log");
stream_set_blocking($stream, true);
stream_set_timeout($stream, 2);
$stream_err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
print_r(array("stdio" => stream_get_contents($stream), "stderr" => stream_get_contents($stream_err)));
$sql->beginTransaction();
foreach ($objects as $object) {
$sql->query("INSERT INTO abuse_rconlog (dt, action, ip) VALUES (?, ?, ?)", $object->toSQLParams());
}
$sql->commit();
$objects = array();
$arr = file_get_contents(__DIR__ . "/login.log");
//die(print_r($arr));
$array = explode("\n", $arr);
foreach ($array as $line) {
$ip = $dt = $player = $adminLevel = null;
$split = preg_split("/[\s]+/", $line);
if (sizeof($split) < 4) {
continue; //probably empty line, weird shit.
}
//read up until len of array - 1, that should be "by"
$dt = new DateTime($split[0] . " " . $split[1]);
//2nd is action, read until sizeof array -2
//ip and powers is easy.
$ip = rtrim($split[sizeof($split) - 1], ".");
$adminLevel = LoginLog::textToAdmin(rtrim($split[sizeof($split) - 3], "."));
//player starts from 4th
$player = substr($split[4], 1); //remove starting '
for ($i = 5; $i < sizeof($split); $i++) {
$tmp = $split[$i];
if (trim($tmp) === "has" && sizeof($split) > ($i + 2) && $split[$i + 1] === "been" && $split[$i + 2] === "granted") {
$player = substr($player, 0, strlen($player) - 1); //remove trailing '
break; //got name
}
$player .= " $tmp";
}
$objects[] = new LoginLog($dt, $player, $adminLevel, $ip);
//$objects[] = new LoginLog();
}
$stream = ssh2_exec($handle, "echo -n > /var/www/xxxx/login.log");
stream_set_blocking($stream, true);
stream_set_timeout($stream, 2);
$stream_err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
print_r(array("stdio" => stream_get_contents($stream), "stderr" => stream_get_contents($stream_err)));
$sql->beginTransaction();
foreach ($objects as $object) {
$sql->query("INSERT INTO abuse_loginlog (dt, player, adminLevel, ip) VALUES (?, ?, ?, ?)", $object->toSQLParams());
}
$sql->commit();
ssh2_disconnect($handle);