diff options
Diffstat (limited to 'modules-available/statistics/api.inc.php')
-rw-r--r-- | modules-available/statistics/api.inc.php | 166 |
1 files changed, 119 insertions, 47 deletions
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php index 30d1fda9..18a58a77 100644 --- a/modules-available/statistics/api.inc.php +++ b/modules-available/statistics/api.inc.php @@ -11,17 +11,17 @@ if (substr($ip, 0, 7) === '::ffff:') $ip = substr($ip, 7); * Power/hw/usage stats */ -if ($type{0} === '~') { +if ($type[0] === '~') { // UUID is mandatory $uuid = Request::post('uuid', '', 'string'); $macaddr = Request::post('macaddr', false, 'string'); if ($macaddr !== false) { $macaddr = strtolower(str_replace(':', '-', $macaddr)); - if (strlen($macaddr) !== 17 || $macaddr{2} !== '-') { + if (strlen($macaddr) !== 17 || $macaddr[2] !== '-') { $macaddr = false; } } - if ($macaddr !== false && $uuid{8} !== '-' && substr($uuid, 0, 16) === '000000000000001-') { + if ($macaddr !== false && $uuid[8] !== '-' && substr($uuid, 0, 16) === '000000000000001-') { $uuid = 'baad1d00-9491-4716-b98b-' . str_replace('-', '', $macaddr); } if (strlen($uuid) !== 36 || !preg_match('/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i', $uuid)) { @@ -31,7 +31,8 @@ if ($type{0} === '~') { // External mode of operation? $mode = Request::post('mode', false, 'string'); $NOW = time(); - $old = Database::queryFirst('SELECT clientip, logintime, lastseen, lastboot, state, mbram, cpumodel, live_memfree, live_swapfree, live_tmpfree + $old = Database::queryFirst('SELECT clientip, locationid, logintime, lastseen, lastboot, state, mbram, + cpumodel, live_memfree, live_swapfree, live_tmpfree FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid)); if ($old !== false) { settype($old['logintime'], 'integer'); @@ -61,10 +62,26 @@ if ($type{0} === '~') { if (!is_string($hostname) || $hostname === $ip) { $hostname = ''; } - $data = Util::cleanUtf8(Request::post('data', '', 'string')); + $json = false; + $data = Util::cleanUtf8(Request::post('json', '', 'string')); + if (!empty($data) && $data[0] === '{') { + $json = json_decode($data, true); + if (!is_array($json)) { + $json = false; + } else { + $json['cpu'] = [ + 'sockets' => Request::post('sockets', 0, 'int'), + 'cores' => $realcores, + 'threads' => Request::post('vcores', 0, 'int'), + ]; + } + } + if ($json === false) { + $data = Util::cleanUtf8(Request::post('data', '', 'string')); + } // Prepare insert/update to machine table $new = array( - 'uuid' => $uuid, + 'machineuuid'=> $uuid, 'macaddr' => $macaddr, 'clientip' => $ip, 'lastseen' => $NOW, @@ -86,7 +103,7 @@ if ($type{0} === '~') { $res = Database::exec('INSERT INTO machine ' . '(machineuuid, macaddr, clientip, firstseen, lastseen, logintime, position, lastboot, realcores, mbram,' . ' kvmstate, cpumodel, systemmodel, id44mb, badsectors, data, hostname, state) VALUES ' - . "(:uuid, :macaddr, :clientip, :firstseen, :lastseen, 0, '', :lastboot, :realcores, :mbram," + . "(:machineuuid, :macaddr, :clientip, :firstseen, :lastseen, 0, '', :lastboot, :realcores, :mbram," . ' :kvmstate, :cpumodel, :systemmodel, :id44mb, :badsectors, :data, :hostname, :state)', $new, true); if ($res === false) { die("Concurrent insert, ignored. (RESULT=0)\n"); @@ -118,9 +135,9 @@ if ($type{0} === '~') { . ' id44mb = :id44mb,' . ' live_tmpsize = 0, live_swapsize = 0, live_memsize = 0, live_cpuload = 255, live_cputemp = 0,' . ' badsectors = :badsectors,' - . ' data = :data,' + . ' data = ' . ($json !== false ? ':data' : "If(Left(data, 1) = '{', data, :data)") . ',' . ' state = :state ' - . " WHERE machineuuid = :uuid AND state = :oldstate AND lastseen = :oldlastseen", $new); + . " WHERE machineuuid = :machineuuid AND state = :oldstate AND lastseen = :oldlastseen", $new); if ($res === 0) { die("Concurrent update, ignored. (RESULT=0)\n"); } @@ -146,7 +163,17 @@ if ($type{0} === '~') { if (($old === false || $old['clientip'] !== $ip) && Module::isAvailable('locations')) { // New, or ip changed (dynamic pool?), update subnetlicationid - Location::updateMapIpToLocation($uuid, $ip); + $loc = Location::updateMapIpToLocation($uuid, $ip); + $new['locationid'] = $loc; // For Filter Event + } + + if ($json !== false) { + $ret = HardwareParser::parseMachine($uuid, $json); + if ($ret !== null) { + // This data is more accurate and ends up in the DB anyways, so use it for event filtering too + $new['id44mb'] = $ret['id44mb']; + $new['id45mb'] = $ret['id45mb']; + } } // Check for suspicious hardware changes @@ -155,16 +182,31 @@ if ($type{0} === '~') { // Log potential crash if ($old['state'] === 'IDLE' || $old['state'] === 'OCCUPIED') { - writeClientLog('machine-mismatch-poweron', 'Poweron event, but previous known state is ' . $old['state'] - . '. Free RAM: ' . Util::readableFileSize($old['live_memfree'], -1, 2) - . ', free Swap: ' . Util::readableFileSize($old['live_swapfree'], -1, 2) - . ', free ID44: ' . Util::readableFileSize($old['live_tmpfree'], -1, 2)); + if (Module::isAvailable('syslog')) { + ClientLog::write($new, 'machine-mismatch-poweron', + 'Poweron event, but previous known state is ' . $old['state'] + . '. Free RAM: ' . Util::readableFileSize($old['live_memfree'], -1, 2) + . ', free Swap: ' . Util::readableFileSize($old['live_swapfree'], -1, 2) + . ', free ID44: ' . Util::readableFileSize($old['live_tmpfree'], -1, 2)); + } } + // Add anything not present in $new from $old + $new += $old; + $new['oldlastboot'] = $old['lastboot']; + } else { + // First boot, mock some important fields for event log filtering + $new['oldlastboot'] = 0; + $new['oldlastseen'] = 0; + $new['oldstate'] = 'OFFLINE'; } + unset($new['data']); + EventLog::applyFilterRules($type, $new); + // Write statistics data } else if ($type === '~runstate') { + // Usage (occupied/free) $sessionLength = 0; $strUpdateBoottime = ''; @@ -174,10 +216,14 @@ if ($type{0} === '~') { } $used = Request::post('used', 0, 'integer'); $params = array( - 'uuid' => $uuid, + 'machineuuid' => $uuid, 'oldlastseen' => $old['lastseen'], 'oldstate' => $old['state'], ); + if ($NOW - $old['lastseen'] < 10 && ($old['state'] === 'OFFLINE' || $old['state'] === 'STANDBY')) { + // Avoid racing calls to ~runstate updates while/after we send a ~poweroff or ~suspend + die("OK.\n"); + } if ($old['state'] === 'OFFLINE') { // This should never happen -- we expect a poweron event before runstate, which would set the state to IDLE // So it might be that the poweron event got lost, or that a couple of runstate events got lost, which @@ -199,14 +245,18 @@ if ($type{0} === '~') { 'memfree', 'tmpfree', 'swapfree', 'id45free', 'cpuload', 'cputemp'] as $item) { $liveVal = Request::post($item, false, 'int'); - if ($liveVal !== false) { - $strUpdateBoottime .= ' live_' . $item . ' = :_' . $item . ', '; + if ($liveVal !== false && $liveVal >= 0) { + $strUpdateBoottime .= ' live_' . $item . ' = :live_' . $item . ', '; if ($item === 'cpuload' || $item === 'cputemp') { $liveVal = round($liveVal); } else { $liveVal = ceil($liveVal / 1024); } - $params['_' . $item] = $liveVal; + $max = ($item === 'cpuload') ? 100 : (2 ** 31); + if ($liveVal > $max) { + $liveVal = $max; + } + $params['live_' . $item] = $liveVal; } } if (($runmode = Request::post('runmode', false, 'string')) !== false) { @@ -222,7 +272,7 @@ if ($type{0} === '~') { $res = Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),' . $strUpdateBoottime . " logintime = 0, currentuser = NULL, state = 'IDLE' " - . " WHERE machineuuid = :uuid AND lastseen = :oldlastseen AND state = :oldstate", + . " WHERE machineuuid = :machineuuid AND lastseen = :oldlastseen AND state = :oldstate", $params); } elseif ($used === 1 && $old['state'] !== 'OCCUPIED') { // Machine is in use, was free before @@ -235,7 +285,7 @@ if ($type{0} === '~') { $res = Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),' . $strUpdateBoottime . " logintime = UNIX_TIMESTAMP(), currentuser = :user, currentsession = NULL, state = 'OCCUPIED' " - . " WHERE machineuuid = :uuid AND lastseen = :oldlastseen AND state = :oldstate", $params); + . " WHERE machineuuid = :machineuuid AND lastseen = :oldlastseen AND state = :oldstate", $params); } else { $res = 0; } @@ -243,7 +293,7 @@ if ($type{0} === '~') { // Nothing changed, simple lastseen update $res = Database::exec('UPDATE machine SET ' . $strUpdateBoottime - . ' lastseen = UNIX_TIMESTAMP() WHERE machineuuid = :uuid AND lastseen = :oldlastseen AND state = :oldstate', $params); + . ' lastseen = UNIX_TIMESTAMP() WHERE machineuuid = :machineuuid AND lastseen = :oldlastseen AND state = :oldstate', $params); } // Did we update, or was there a concurrent update? if ($res === 0) { @@ -253,7 +303,12 @@ if ($type{0} === '~') { if ($mode === false && $sessionLength > 0 && $sessionLength < 86400*2 && $old['logintime'] !== 0) { Statistics::logMachineState($uuid, $ip, Statistics::SESSION_LENGTH, $old['logintime'], $sessionLength); } + // Client Events + $params['newstate'] = ($used === 0) ? 'IDLE' : 'OCCUPIED'; + EventLog::applyFilterRules($type, $params + $old); + } elseif ($type === '~poweroff') { + if ($old === false) die("Unknown machine.\n"); if ($old['clientip'] !== $ip) { updateIp('poweroff', $uuid, $old, $ip); @@ -267,7 +322,11 @@ if ($type{0} === '~') { Database::exec("UPDATE machine SET logintime = 0, lastseen = UNIX_TIMESTAMP(), state = 'OFFLINE' WHERE machineuuid = :uuid AND state = :oldstate AND lastseen = :oldlastseen", array('uuid' => $uuid, 'oldlastseen' => $old['lastseen'], 'oldstate' => $old['state'])); + + EventLog::applyFilterRules($type, $old); + } elseif ($mode === false && $type === '~screens') { + if ($old === false) die("Unknown machine.\n"); $screens = Request::post('screen', false, 'array'); if (is_array($screens)) { @@ -279,24 +338,24 @@ if ($type{0} === '~') { if (!array_key_exists('name', $screen)) continue; // Filter bogus data - $screen['name'] = Util::cleanUtf8($screen['name']); - $port = Util::cleanUtf8($port); + $screen['name'] = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $screen['name']); + $port = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $port); if (empty($screen['name'])) continue; if (array_key_exists($screen['name'], $hwids)) { $hwid = $hwids[$screen['name']]; } else { - $hwid = (int)Database::insertIgnore('statistic_hw', 'hwid', - array('hwtype' => DeviceType::SCREEN, 'hwname' => $screen['name'])); + $hwid = Database::insertIgnore('statistic_hw', 'hwid', + ['hwtype' => HardwareInfo::SCREEN, 'hwname' => $screen['name']]); $hwids[$screen['name']] = $hwid; } // Now add new entries $keepPair[] = array($hwid, $port); - $machinehwid = Database::insertIgnore('machine_x_hw', 'machinehwid', array( + $machinehwid = Database::insertIgnore('machine_x_hw', 'machinehwid', [ 'hwid' => $hwid, 'machineuuid' => $uuid, 'devpath' => $port, - ), array('disconnecttime' => 0)); + ], ['disconnecttime' => 0]); $validProps = array(); if (count($screen) > 1) { // Screen has additional properties (resolution, size, etc.) @@ -333,20 +392,27 @@ if ($type{0} === '~') { Database::exec("UPDATE machine_x_hw x, statistic_hw h SET x.disconnecttime = UNIX_TIMESTAMP() WHERE x.machineuuid = :uuid AND x.hwid = h.hwid AND h.hwtype = :type AND x.disconnecttime = 0", - array('uuid' => $uuid, 'type' => DeviceType::SCREEN)); + array('uuid' => $uuid, 'type' => HardwareInfo::SCREEN)); } else { // Some screens connected, make sure old entries get removed Database::exec("UPDATE machine_x_hw x, statistic_hw h SET x.disconnecttime = UNIX_TIMESTAMP() - WHERE (x.hwid, x.devpath) NOT IN (:pairs) AND x.disconnecttime = 0 AND h.hwtype = :type + WHERE (x.hwid, x.devpath) NOT IN (:pairs) AND x.hwid = h.hwid AND x.disconnecttime = 0 AND h.hwtype = :type AND x.machineuuid = :uuid", array( 'pairs' => $keepPair, 'uuid' => $uuid, - 'type' => DeviceType::SCREEN, + 'type' => HardwareInfo::SCREEN, )); } + + // Benchmarking + Database::exec("INSERT INTO statistic (dateline, typeid, clientip, machineuuid, username, `data`) + VALUES (UNIX_TIMESTAMP(), :type, :ip, :uuid, '', '')", + ['type' => 'graphical-startup', 'ip' => $ip, 'uuid' => $uuid]); } + } else if ($type === '~suspend') { + // Client entering suspend if ($old === false) die("Unknown machine.\n"); if ($old['clientip'] !== $ip) { @@ -357,10 +423,13 @@ if ($type{0} === '~') { standbysem = If(standbysem < 6, standbysem + 1, 6) WHERE machineuuid = :uuid AND state = :oldstate AND lastseen = :oldlastseen", array('uuid' => $uuid, 'oldlastseen' => $old['lastseen'], 'oldstate' => $old['state'])); + EventLog::applyFilterRules($type, $old); } else { EventLog::info("[suspend] Client $uuid reported switch to standby when it wasn't powered on first. Was: " . $old['state']); } + } else if ($type === '~resume') { + // Waking up from suspend if ($old === false) die("Unknown machine.\n"); if ($old['clientip'] !== $ip) { @@ -379,6 +448,7 @@ if ($type{0} === '~') { Statistics::logMachineState($uuid, $ip, Statistics::SUSPEND_LENGTH, $lastSeen, $duration); } } + EventLog::applyFilterRules($type, $old); } else { EventLog::info("[resume] Client $uuid reported wakeup from standby when it wasn't logged as being in standby. Was: " . $old['state']); } @@ -413,21 +483,9 @@ function writeStatisticLog($type, $username, $data) )); } -function writeClientLog($type, $description) -{ - global $ip, $uuid; - Database::exec('INSERT INTO clientlog (dateline, logtypeid, clientip, machineuuid, description, extra) VALUES (UNIX_TIMESTAMP(), :type, :client, :uuid, :description, :longdesc)', array( - 'type' => $type, - 'client' => $ip, - 'description' => $description, - 'longdesc' => '', - 'uuid' => $uuid, - )); -} - - // For backwards compat, we require the . prefix -if ($type{0} === '.') { +if ($type[0] === '.') { + $data = false; if ($type === '.vmchooser-session') { $user = Util::cleanUtf8(Request::post('user', 'unknown', 'string')); $loguser = Request::post('loguser', 0, 'int') !== 0; @@ -437,21 +495,35 @@ if ($type{0} === '.') { Database::exec("UPDATE machine SET currentuser = :user, currentsession = :session WHERE clientip = :ip", compact('user', 'session', 'ip')); writeStatisticLog('.vmchooser-session-name', ($loguser ? $user : 'anonymous'), $sessionName); + $data = [ + 'clientip' => $ip, + 'sessionName' => $sessionName, + 'sessionUuid' => $sessionUuid, + 'session' => $session, + ]; } else { if (!isset($_POST['description'])) die('Missing options..'); $description = $_POST['description']; // and username embedded in message if (preg_match('#^\[([^\]]+)\]\s*(.*)$#m', $description, $out)) { writeStatisticLog($type, $out[1], $out[2]); + $data = [ + 'clientip' => $ip, + 'user' => $out[1], + 'description' => $out[2], + ]; } } + if ($data !== false) { + EventLog::applyFilterRules($type, $data); + } } /** * @param array $old row from DB with client's old data * @param array $new new data to be written */ -function checkHardwareChange($old, $new) +function checkHardwareChange(array $old, array $new): void { if ($new['mbram'] !== 0) { if ($new['mbram'] < 6200) { @@ -463,10 +535,10 @@ function checkHardwareChange($old, $new) } if ($ram1 !== $ram2) { $word = $ram1 > $ram2 ? 'decreased' : 'increased'; - EventLog::warning('[poweron] Client ' . $new['uuid'] . ' (' . $new['clientip'] . "): RAM $word from {$ram1}GB to {$ram2}GB"); + EventLog::warning('[poweron] Client ' . $new['machineuuid'] . ' (' . $new['clientip'] . "): RAM $word from {$ram1}GB to {$ram2}GB"); } if (!empty($old['cpumodel']) && !empty($new['cpumodel']) && $new['cpumodel'] !== $old['cpumodel']) { - EventLog::warning('[poweron] Client ' . $new['uuid'] . ' (' . $new['clientip'] . "): CPU changed from '{$old['cpumodel']}' to '{$new['cpumodel']}'"); + EventLog::warning('[poweron] Client ' . $new['machineuuid'] . ' (' . $new['clientip'] . "): CPU changed from '{$old['cpumodel']}' to '{$new['cpumodel']}'"); } } } |