<?php
declare(ticks = 1);
if (! function_exists('pcntl_fork')) die('PCNTL functions not available on this PHP installation');
echo "Start of program!\n";
if (isset($argv[1])) {
if ($argv[1] == 'verbose' || $argv[1] == '-v') {
$verbose = 1;
} elseif ($argv[1] == 'vv' || $argv[1] == '-vv') {
$verbose = 2;
} else {
$verbose = 0;
}
} else {
$verbose = 0;
}
require_once("/home/pugbot/tracker/q3query.php");
require_once("/home/pugbot/tracker/align.php");
include("/home/pugbot/tracker/dbconfig.php");
// connect to db
function mcrypt($data) {
$key = 'ASsadfEYHr345REtrte74hHFGDsret7245dFDGDFer3D7hhs4467H85p';
$iv = '82952684';
$todec = base64_decode($data);
$decrypted = mcrypt_decrypt( MCRYPT_BLOWFISH, $key, $todec, MCRYPT_MODE_CBC, $iv );
return $decrypted;
}
function send_rcon($command, $server_host, $server_port, $rcon_pass) {
if ($server_host == "" || !isset($server_host)) {
return FALSE;
}
// Set server, port and RCON for server to be used.
$q = new q3query($server_host, $server_port);
$q->set_rconpassword($rcon_pass);
// Make sure variables are clean.
$failed = 0;
$rconresponse = 0;
while ($failed != 8 && !($rconresponse)) {
$q->rcon($command);
$rconresponse = $q->get_response();
//echo $rconresponse;
$failed++;
if (!($rconresponse)) { sleep(1); }
}
if ($failed == 8) {
return FALSE;
} else {
return $rconresponse;
}
}
function sig_handler($signo) {
if($signo == SIGUSR1) {
// Handle SIGUSR1
echo "SIG: ".$signo."\n";
} elseif($signo == SIGTERM) {
// Handle SIGTERM
echo "SIG: ".$signo."\n";
$sigterm = true;
} elseif($signo == SIGHUP) {
// Handle SIGHUP
echo "SIG: ".$signo."\n";
$sighup = true;
}
}
// Function for thread to track each server
function track_server($server_to, $pid, $verbose) {
// Setup the align class for thread
$align = new align($verbose);
// Include DB info
include("/home/pugbot/tracker/dbconfig.php");
//$align->align_d_echo_a($db);
// Make the database connection for this thread.
$server_connection = mysql_connect($db['host'], $db['user'], $db['pass'], TRUE) or die ("Unable to connect!");
mysql_select_db($db['db']) or die ("Unable to select database!");
startserver:
// setup the vars
$server_id = $server_to;
$server_query = mysql_query("SELECT * FROM `servers` WHERE `server_id`='$server_id' AND `status`!='RCONFAIL'") or die("unable to query table! track_server, line: 84 ".$server_id);
$server = mysql_fetch_array($server_query);
$align->align_s_echo_a(array("SERVER_ID"=>$server_id,"TRACKING!"=>"STARTED!"));
if (!$server) {
$align->align_s_echo_a(array("SERVER_ID"=>$server_id,"RCON!"=>"FAILED!"));
} else {
$server_host = gethostbyname($server['host']);
$server_port = $server['port'];
$rcon_pass_enc = $server['rcon'];
$rcon_pass = mcrypt($rcon_pass_enc);
$rcon_pass = trim($rcon_pass,"\0");
$thread_name = "Thread for server ID ".$server_id;
// Set the process title (shows up in "ps aux")
setproctitle($thread_name);
// Infinite tracking loop, never stop, at least try not to.
while (TRUE) {
// Like to sleep a lot with urt rcon commands to help prevent rcon flood conditions.
sleep(1);
// Set the pid in the servers table
mysql_query("UPDATE `servers` SET `tracker_pid`=0 WHERE `server_id`='$server_id' ");
// Echo to console some basic status information.
$align->align_d_echo_a(array("SERVER_ID"=>$server_id,"UPDATING!"=>"STARTED!"));
// Set the time for this pass on tracking
$start_time = time();
// fetch connected players via rcon
$command = "status";
$status_response = send_rcon($command, $server_host, $server_port, $rcon_pass);
//$align->align_s_echo_a(array("SERVER_ID"=>$server_id,"RCON RESPONSE!"=>"--".$status_response."--"));
/* If statements for checking $status_response */
if (isset($status_response)) {
if ($status_response === FALSE || $status_response == FALSE || strpos($status_response, "Bad rconpassword.")) {
mysql_query("UPDATE `servers` SET `status`=\"RCONFAIL\" WHERE `server_id`='$server_id' ");
// Some debug output
$align->align_s_echo_a(array("SERVER_ID"=>$server_id,"STATUS!"=>"FAILED!"));
goto startserver;
} else {
// Some debug output
$align->align_s_echo_a(array("SERVER_ID"=>$server_id,"STATUS!"=>"SUCCEEDED!"));
//echo $status_response;
}
} else {
// Some debug output
$align->align_s_echo_a(array("SERVER_ID"=>$server_id,"STATUS!"=>"FAILED!"));
goto startserver;
}
// Like to sleep a lot with urt rcon commands to help prevent rcon flood conditions.
sleep(1);
// Some magic TheRick found/came up with.
preg_match_all('/^\s+(\S{1,2}?)\s+(\S{1,4})\s+(\S{1,3})\s+(.*)\^7\s+(\S+)\s+(\S+):(\S+)\s+(\S+)\s+(\S+)$/m', $status_response,$type);
preg_match('/^map:\s+(.*)$/m', $status_response, $aMap);
// This line sets the map on the server from the rcon status
if (isset($aMap[1])) {
$map = $aMap[1];
mysql_query(" UPDATE `servers` SET `current_map`='$map' WHERE `server_id`='$server_id' ");
} else {
$align->align_v_echo_a($aMap);
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"STATUS"=>$status_response));
}
// Setup rcon to grab sv_hostname
$command = "sv_hostname";
// Send sv_hostname command
$sv_hostname_response = send_rcon($command, $server_host, $server_port, $rcon_pass);
sleep(1);
$sv_first_split = split("is:\"", $sv_hostname_response);
if (isset($sv_first_split[1])) {
$sv_second_split = split("\^7\" default:",$sv_first_split[1]);
$sv_hostname = mysql_real_escape_string($sv_second_split[0]);
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"sv_hostname"=>$sv_hostname_response));
} else {
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"sv_hostname"=>$sv_hostname_response));
$sv_hostname = "Bad server name query";
// Set num players and sv_hostname in servers database
mysql_query("UPDATE `servers` SET `name`='$sv_hostname' WHERE `server_id`='$server_id'");
goto startserver;
}
// Setup rcon to grab sv_maxclients
$command = "sv_maxclients";
// Send sv_hostname command
$sv_maxclients_response = send_rcon($command, $server_host, $server_port, $rcon_pass);
sleep(1);
$sv_first_split = split("is:\"", $sv_maxclients_response);
if (isset($sv_first_split[1])) {
$sv_second_split = split("\^7\" default:",$sv_first_split[1]);
$sv_maxclients = mysql_real_escape_string($sv_second_split[0]);
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"sv_maxclients_raw"=>$sv_maxclients_response));
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"sv_maxclients"=>$sv_maxclients));
} else {
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"sv_maxclients_raw"=>$sv_maxclients_response));
$sv_maxclients = "0";
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"sv_maxclients"=>$sv_maxclients));
// Set num players and sv_maxclients in servers database
mysql_query("UPDATE `servers` SET `slots`='$sv_maxclients' WHERE `server_id`='$server_id'");
goto startserver;
}
// Setup rcon to grab sv_hostname
$command = "g_gametype";
// Send sv_hostname command
$gametype_response = send_rcon($command, $server_host, $server_port, $rcon_pass);
sleep(1);
$gametype_first_split = split("is:\"", $gametype_response);
if (isset($gametype_first_split[1])) {
$gametype_second_split = split("\^7",$gametype_first_split[1]);
$gametype = $gametype_second_split[0];
} else {
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"g_gametype"=>$gametype_response));
$gametype = 0;
// Set num players and sv_hostname in servers database
mysql_query("UPDATE `servers` SET `gametype`='$gametype' WHERE `server_id`='$server_id'");
goto startserver;
}
// Clear the user records from temp table
mysql_query( "DELETE FROM `tracker_active` WHERE `server_id`='$server_id'" );
// Count the number of players in server, by counting the number of pids
$count = count($type[1]);
// Loop for each player found
for( $i=1; $i<=$count ;$i++ ) {
// Player slots start at zero, but loop starts at 1, subtract 1.
$num = $i-1;
// Player pid
$pid = $type[1][$num];
// Player score
$score = $type[2][$num];
// Player ping
$ping = $type[3][$num];
// Player name, must be mysql escaped as a name could actually be an injection point
$name = mysql_real_escape_string($type[4][$num]);
// Lastmsg
$lastmsg = $type[5][$num];
// Player's IP
$ip = $type[6][$num];
// Players net_port
$port = $type[7][$num];
// Players qport
$qport = $type[8][$num];
// Players connected rate
$rate = $type[9][$num];
// split up the IP into 4 parts (octets)
$ip_array = explode(".",$ip);
$ip1 = $ip_array[0];
$ip2 = $ip_array[1];
$ip3 = $ip_array[2];
$ip4 = $ip_array[3];
// get the ipkey of IP
$ipkey = (($ip1*256+$ip2)*256+$ip3)*256 + $ip4;
$truename = stripslashes($name);
// Setup the rcon dumpuser for this pid
$command = "dumpuser \"".$truename."\"";
// Send dumpuser rcon command
$dump_response = send_rcon($command, $server_host, $server_port, $rcon_pass);
// Sleep some more after rcon command
sleep(1);
// Set guid blank to make sure there isn't a left over in memory
$guid = "";
// Do the same for sacc_maxpackets
$sacc_maxpackets = 0;
// Split the rcon response from the dumpuser command
$peices = preg_split("/[\s,]+/", $dump_response, -1, PREG_SPLIT_NO_EMPTY);
// Foreach piece in $pieces, run this loop, which gets guid and sacc_maxpackets from dumpuser
foreach ($peices as $key=>$piece) {
// If the piece == guid then set the guid
if ($piece == "cl_guid") {
// Make sure to escape the guid, adding +1 to the key to get the value
$guid = mysql_real_escape_string($peices[($key + 1)]);
// Some debug output
//echo $align->left("SERVER_ID", 10).": ".$align->left($server_id, 7).$align->left("NAME",15).": ".$align->left($name, 0).$align->left("GUID FOUND", 0).": ".$guid."\n";
}
// If the piece == sacc_maxpackets then set the variable
if ($piece == "sacc_maxpackets") {
// Set maxpackets, adding +1 to the key to get the value
$sacc_maxpackets = $peices[($key + 1)];
// Some debug output
$align->align_s_echo_a(array("SERVER_ID"=>$server_id,"SACC FOUND"=>$name));
}
}
if (!strpos($dump_response,"is not on the") && $guid != "") {
// If the IP != the ip of the GTV bot then add to the database
if ($ip != "208.93.223.133") {
// Some debug output
$align->align_d_echo_a(array("SERVER_ID"=>$server_id,"NAME"=>$name,"ADDING IP TO DB"=>$ip));
// Make the mysql query to add the player to the database
mysql_query(" INSERT INTO `tracker_active` (`key`, `server_id`, `pid`, `score`, `ping`, `name`, `lastmsg`, `ip`,`ipkey`, `port`, `qport`, `rate`, `GUID`, `sacc_maxpackets`) VALUES (NULL, '$server_id', '$pid', '$score', '$ping', '$name', '$lastmsg', '$ip','$ipkey', '$port', '$qport', '$rate', '$guid', '$sacc_maxpackets'); ");
} else {
// Some debug output
$align->align_d_echo_a(array("SERVER_ID"=>$server_id,"NAME"=>$name,"*NOT ADDING IP TO DB"=>$ip));
}
} elseif ($guid == "") {
if (!isset($sacc)) { $sacc = 0; }
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"NAME"=>$name,"SACC"=>$sacc,"SLOT"=>$pid,"GUID BLANK"=>$dump_response));
// Setup rcon to grab sv_hostname
$command = "kick ".$pid;
// Send sv_hostname command
$gametype_response = send_rcon($command, $server_host, $server_port, $rcon_pass);
sleep(1);
} else {
if (!isset($sacc)) { $sacc = 0; }
$align->align_v_echo_a(array("SERVER_ID"=>$server_id,"NAME"=>$name,"SACC"=>$sacc,"SLOT"=>$pid,"GUID NOT"=>$dump_response));
}
}
// Select all the players we just added to the temp database table for this server
$update_players = mysql_query("SELECT * FROM `tracker_active` WHERE `server_id`='$server_id'");
// Get num players in server
$server_num_players = mysql_num_rows($update_players);
// Set num players and sv_hostname in servers database
mysql_query("UPDATE `servers` SET `gametype`='$gametype',`name`='$sv_hostname',`players`='$server_num_players',`slots`='$sv_maxclients' WHERE `server_id`='$server_id'");
// If the query failed, output some debug info, else query successful update the new temp table
if (!$update_players) {
// Output some debug info
$align->align_s_echo("FAILED SERVER_ID",$server_id);
} else {
// Delete all players from the second temp table for this server
mysql_query( "DELETE FROM `tracker_status` WHERE `server_id`='$server_id'" );
// Run this loop for each player, inserting them into the second temp table
while ($player_info = mysql_fetch_array($update_players)) {
//$align->align_d_echo_a($player_info);
// Set the variables to be set in the second temp table
$name = mysql_real_escape_string($player_info['name']);
$server_id = $player_info['server_id'];
$pid = $player_info['pid'];
$score = $player_info['score'];
$ping = $player_info['ping'];
$lastmsg = $player_info['lastmsg'];
$ip = $player_info['ip'];
$ipkey = $player_info['ipkey'];
$port = $player_info['port'];
$qport = $player_info['qport'];
$rate = $player_info['rate'];
$guid = mysql_real_escape_string($player_info['GUID']);
$sacc_maxpackets = $player_info['sacc_maxpackets'];
// Add the player to the second temp table here, second temp table gets handled by main thread from here
mysql_query(" INSERT INTO `tracker_status` (`key`, `server_id`, `pid`, `score`, `ping`, `name`, `lastmsg`, `ip`,`ipkey`, `port`, `qport`, `rate`, `GUID`, `sacc_maxpackets`) VALUES (NULL, '$server_id', '$pid', '$score', '$ping', '$name', '$lastmsg', '$ip','$ipkey', '$port', '$qport', '$rate', '$guid', '$sacc_maxpackets'); ");
}
}
// Count how long it took to track all players on server
$total_time = time() - $start_time;
// Some debug output
$align->align_d_echo_a(array("SERVER_ID"=>$server_id,"DONE UPDATING!"=>"TOOK:","TIME"=>$total_time));
}
}
}
function keep_tracking($verbose) {
include("/home/pugbot/tracker/dbconfig.php");
$align = new align($verbose);
while (TRUE) {
$start_time = time();
$align->align_s_echo("STARTING SERVERS!",$start_time);
$connection = mysql_connect($db['host'], $db['user'], $db['pass'], TRUE) or die ("Unable to connect!");
mysql_select_db($db['db']) or die ("Unable to select database! keep_tracking".$db['db']);
// grab players in servers
$status_q = mysql_query(" SELECT * FROM `tracker_status` ");
// Get all servers and try to check on the pid
$servers_q = mysql_query(" SELECT * FROM `servers` WHERE `status`!='RCONFAIL'");
// For each server get pid n check
while ($server = mysql_fetch_array($servers_q)) {
// Set the $pid
$pid_count = $server['tracker_pid'];
// Set server_id
$server_id = $server['server_id'];
if ($pid_count > 20) {
$pid = pcntl_fork();
if ($pid == 0) {
$align->align_s_echo("Forked child",$pid);
} elseif ($pid == -1) {
$align->align_s_echo("Failed to fork!",$pid);
} else {
$align->align_s_echo("Child started!",$pid);
track_server($server_id, $pid, $verbose);
exit(35);
}
$pid_count = 0;
} else {
// add +1 to the pid
$pid_count++;
}
// update the pid in the servers table
mysql_query("UPDATE `servers` SET `tracker_pid`='$pid_count' WHERE `server_id`='$server_id'");
// Output some debug info
$align->align_s_echo("TRACKER CHECKING!",$server_id);
//$align->align_d_echo_a(array("SERVER_ID"=>$server_id,"TRACKER CHECKING!"=>"COMPLETE!"));
}
// start timer
$timer_start_urtplayers = time();
// for each player, do the following
while ($status = mysql_fetch_array($status_q)) {
// setup the vars
$name = mysql_real_escape_string($status['name']);
$server_id = $status['server_id'];
$pid = $status['pid'];
$score = $status['score'];
$ping = $status['ping'];
$lastmsg = $status['lastmsg'];
$ip = $status['ip'];
$ipkey = $status['ipkey'];
$port = $status['port'];
$qport = $status['qport'];
$rate = $status['rate'];
$guid = mysql_real_escape_string($status['GUID']);
$sacc_maxpackets = $status['sacc_maxpackets'];
$ip_search = mysql_query(" SELECT `tuser_id` FROM `tracker_aliases` WHERE `ip`='$ip' AND `alias`='$name' AND `GUID`='$guid' AND `server_id`='$server_id'");
$user_array = mysql_fetch_array($ip_search);
$tuser = $user_array[0];
$rows = mysql_num_rows($ip_search);
$align->align_d_echo_a(array("SERVER_ID"=>$server_id,"TRACKER UPDATING!"=>"STARTING!"));
// if it matches then do this stuff
if ($rows != 0) {
// check if alias has been on this IP before
$alias_check = mysql_query(" SELECT * FROM `tracker_aliases` WHERE `alias`='$name' AND `ip`='$ip' AND `GUID`='$guid' AND `server_id`='$server_id'");
// if it has then update the counter
$rows = mysql_num_rows($alias_check);
if ($rows > 0) {
mysql_query(" UPDATE `tracker_aliases` SET `counter`=`counter`+1 , `last_time`='$start_time' WHERE `tuser_id`='$tuser' ");
} elseif ($rows == 0 && $guid != "") {
mysql_query(" INSERT INTO `tracker_aliases`(`alias`,`ipkey`,`counter`,`ip`,`GUID`,`sacc_maxpackets`,`server_id`,`first_time`) VALUES('$name','$ipkey','0','$ip','$guid','$sacc_maxpackets','$server_id','$start_time') ");
}
} elseif ($rows == 0) {
mysql_query(" INSERT INTO `tracker_aliases`(`alias`,`ipkey`,`counter`,`ip`,`GUID`,`sacc_maxpackets`,`server_id`,`first_time`) VALUES('$name','$ipkey','0','$ip','$guid','$sacc_maxpackets','$server_id','$start_time') ");
}
}
$total_time = time() - $start_time;
$align->align_d_echo_a(array("SERVER_ID"=>$server_id,"TRACKER UPDATING!"=>"STARTING!","TIME"=>$total_time));
sleep(5);
mysql_close($connection);
}
}
pcntl_signal(SIGUSR1, "sig_handler");
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
//$connection = mysql_connect($db['host'], $db['user'], $db['pass']) or die ("Unable to connect!");
//mysql_select_db($db['db']) or die ("Unable to select database!");
/*
// grab server information
$server_q = mysql_query(" SELECT * FROM `servers` WHERE `status`<>'badpass' ");
$server_id = array();
// for each server, do the following
while ($server = mysql_fetch_array($server_q)) {
$server_id[] = $server['server_id'];
}
mysql_close($connection);
foreach ($server_id as $server_to) {
$pid = pcntl_fork();
if ($pid == 0) {
echo "Forked child #{$pid}\n\n";
} elseif ($pid == -1) {
echo "Failed to fork! #{$pid}\n\n";
} else {
echo "Child! {$pid} =====================================================\n";
track_server($server_to, $pid);
exit(35);
}
}
*/
setproctitle ( "Slacks multithreaded tracker" );
keep_tracking($verbose);
print "Done! :^) PID: {$pid} \n\n";
?>