diff options
author | Jonathan Bauer | 2016-04-01 16:50:13 +0200 |
---|---|---|
committer | Jonathan Bauer | 2016-04-01 16:50:13 +0200 |
commit | dbc0d9614421e064cc62aacf116ebb783c83f2f3 (patch) | |
tree | 091844b8578ff1d9ac18edfd3cee3e63210133d7 /modules/systemstatus | |
parent | [ldapauth] Add homedir conf to ldap wizard (diff) | |
download | slx-admin-dbc0d9614421e064cc62aacf116ebb783c83f2f3.tar.gz slx-admin-dbc0d9614421e064cc62aacf116ebb783c83f2f3.tar.xz slx-admin-dbc0d9614421e064cc62aacf116ebb783c83f2f3.zip |
[merge] merging c3sl / fr - initial commit
Diffstat (limited to 'modules/systemstatus')
-rw-r--r-- | modules/systemstatus/config.json | 4 | ||||
-rw-r--r-- | modules/systemstatus/module.inc.php | 361 | ||||
-rw-r--r-- | modules/systemstatus/templates/_page.html | 129 | ||||
-rw-r--r-- | modules/systemstatus/templates/addresses.html | 8 | ||||
-rw-r--r-- | modules/systemstatus/templates/diskstat.html | 63 | ||||
-rw-r--r-- | modules/systemstatus/templates/services.html | 6 | ||||
-rw-r--r-- | modules/systemstatus/templates/systeminfo.html | 115 |
7 files changed, 686 insertions, 0 deletions
diff --git a/modules/systemstatus/config.json b/modules/systemstatus/config.json new file mode 100644 index 00000000..d42dc3e3 --- /dev/null +++ b/modules/systemstatus/config.json @@ -0,0 +1,4 @@ +{ + "category":"status", + "enabled":"true" +} diff --git a/modules/systemstatus/module.inc.php b/modules/systemstatus/module.inc.php new file mode 100644 index 00000000..2a5382e1 --- /dev/null +++ b/modules/systemstatus/module.inc.php @@ -0,0 +1,361 @@ +<?php + +class Page_SystemStatus extends Page +{ + + private $rebootTask = false; + + protected function doPreprocess() + { + User::load(); + + if (!User::isLoggedIn()) { + Message::addError('no-permission'); + Util::redirect('?do=Main'); + } + + if (Request::post('action') === 'reboot') { + if (Request::post('confirm') !== 'yep') { + Message::addError('reboot-unconfirmed'); + Util::redirect('?do=SystemStatus'); + } + $this->rebootTask = Taskmanager::submit('Reboot'); + } + } + + protected function doRender() + { + $data = array(); + if (is_array($this->rebootTask) && isset($this->rebootTask['id'])) { + $data['rebootTask'] = $this->rebootTask['id']; + } + Render::addScriptTop('custom'); + Render::addScriptBottom('circles.min'); + Render::addTemplate('_page', $data); + } + + protected function doAjax() + { + User::load(); + + if (!User::isLoggedIn()) + return; + + $action = 'ajax' . Request::any('action'); + if (method_exists($this, $action)) { + $this->$action(); + Message::renderList(); + } else { + echo "Action $action not known in " . get_class(); + } + } + + protected function ajaxDmsdUsers() + { + $ret = Download::asStringPost('http://127.0.0.1:9080/status/fileserver', false, 2, $code); + if ($code != 200) { + Header('HTTP/1.1 502 Internal Server Error'); + die('Internal Server Wurst'); + } + $data = @json_decode($ret, true); + if (is_array($data)) { + $ret = 'Uploads: ' . $data['activeUploads'] . ', Downloads: ' . $data['activeDownloads']; + } else { + $ret = '???'; + } + die($ret); + } + + protected function ajaxDiskStat() + { + $task = Taskmanager::submit('DiskStat'); + if ($task === false) + return; + $task = Taskmanager::waitComplete($task, 3000); + + if (!isset($task['data']['list']) || empty($task['data']['list'])) { + Taskmanager::addErrorMessage($task); + return; + } + $store = Property::getVmStoreUrl(); + $storeUsage = false; + $systemUsage = false; + if ($store !== false) { + if ($store === '<local>') + $storePoint = '/'; + else + $storePoint = CONFIG_VMSTORE_DIR; + // Determine free space + foreach ($task['data']['list'] as $entry) { + if ($entry['mountPoint'] === $storePoint) { + $storeUsage = array( + 'percent' => $entry['usedPercent'], + 'size' => Util::readableFileSize($entry['sizeKb'] * 1024), + 'free' => Util::readableFileSize($entry['freeKb'] * 1024), + 'color' => $this->usageColor($entry['usedPercent']) + ); + } + if ($entry['mountPoint'] === '/') { + $systemUsage = array( + 'percent' => $entry['usedPercent'], + 'size' => Util::readableFileSize($entry['sizeKb'] * 1024), + 'free' => Util::readableFileSize($entry['freeKb'] * 1024), + 'color' => $this->usageColor($entry['usedPercent']) + ); + } + } + $data = array( + 'store' => $storeUsage, + 'system' => $systemUsage + ); + // Determine if proper vm store is being used + if ($store !== '<local>') { + $data['storeMissing'] = $store; + } + foreach ($task['data']['list'] as $entry) { + if ($entry['mountPoint'] !== CONFIG_VMSTORE_DIR) + continue; + if ($store !== $entry['fileSystem']) { + $data['wrongStore'] = $entry['fileSystem']; + break; + } + $data['storeMissing'] = false; + } + } else { + $data['notConfigured'] = true; + } + echo Render::parse('diskstat', $data); + } + + protected function ajaxAddressList() + { + $task = Taskmanager::submit('LocalAddressesList'); + if ($task === false) + return; + $task = Taskmanager::waitComplete($task, 3000); + + if (!isset($task['data']['addresses']) || empty($task['data']['addresses'])) { + Taskmanager::addErrorMessage($task); + return; + } + + $sort = array(); + $primary = Property::getServerIp(); + foreach ($task['data']['addresses'] as &$addr) { + $sort[] = $addr['type'] . $addr['ip']; + if ($addr['ip'] === $primary) + $addr['primary'] = true; + } + array_multisort($sort, SORT_STRING, $task['data']['addresses']); + echo Render::parse('addresses', array( + 'addresses' => $task['data']['addresses'] + )); + } + + private function sysInfo() + { + $data = array(); + $memInfo = file_get_contents('/proc/meminfo'); + $stat = file_get_contents('/proc/stat'); + preg_match_all('/\b(\w+):\s+(\d+)\s/s', $memInfo, $out, PREG_SET_ORDER); + foreach ($out as $e) { + $data[$e[1]] = $e[2]; + } + if (preg_match('/\bcpu\s+(?<user>\d+)\s+(?<nice>\d+)\s+(?<system>\d+)\s+(?<idle>\d+)\s+(?<iowait>\d+)\s+(?<irq>\d+)\s+(?<softirq>\d+)(\s|$)/', $stat, $out)) { + $data['CpuTotal'] = $out['user'] + $out['nice'] + $out['system'] + $out['idle'] + $out['iowait'] + $out['irq'] + $out['softirq']; + $data['CpuIdle'] = $out['idle'] + $out['iowait']; + $data['CpuSystem'] = $out['irq'] + $out['softirq']; + } + return $data; + } + + protected function ajaxSystemInfo() + { + $cpuInfo = file_get_contents('/proc/cpuinfo'); + $uptime = file_get_contents('/proc/uptime'); + $cpuCount = preg_match_all('/\bprocessor\s/', $cpuInfo, $out); + //$cpuCount = count($out); + $data = array( + 'cpuCount' => $cpuCount, + 'memTotal' => '???', + 'memFree' => '???', + 'swapTotal' => '???', + 'swapUsed' => '???', + 'uptime' => '???' + ); + if (preg_match('/^(\d+)\D/', $uptime, $out)) { + $data['uptime'] = floor($out[1] / 86400) . ' ' . Dictionary::translate('lang_days') . ', ' . floor(($out[1] % 86400) / 3600) . ' ' . Dictionary::translate('lang_hours'); // TODO: i18n + } + $info = $this->sysInfo(); + if (isset($info['MemTotal']) && isset($info['MemFree']) && isset($info['SwapTotal'])) { + $data['memTotal'] = Util::readableFileSize($info['MemTotal'] * 1024); + $data['memFree'] = Util::readableFileSize(($info['MemFree'] + $info['Buffers'] + $info['Cached']) * 1024); + $data['memPercent'] = 100 - round((($info['MemFree'] + $info['Buffers'] + $info['Cached']) / $info['MemTotal']) * 100); + $data['swapTotal'] = Util::readableFileSize($info['SwapTotal'] * 1024); + $data['swapUsed'] = Util::readableFileSize(($info['SwapTotal'] - $info['SwapFree']) * 1024); + $data['swapPercent'] = 100 - round(($info['SwapFree'] / $info['SwapTotal']) * 100); + $data['swapWarning'] = ($data['swapPercent'] > 50 || ($info['SwapTotal'] - $info['SwapFree']) > 200000); + } + if (isset($info['CpuIdle']) && isset($info['CpuSystem']) && isset($info['CpuTotal'])) { + $data['cpuLoad'] = 100 - round(($info['CpuIdle'] / $info['CpuTotal']) * 100); + $data['cpuSystem'] = round(($info['CpuSystem'] / $info['CpuTotal']) * 100); + $data['cpuLoadOk'] = true; + $data['CpuTotal'] = $info['CpuTotal']; + $data['CpuIdle'] = $info['CpuIdle']; + } + echo Render::parse('systeminfo', $data); + } + + protected function ajaxSysPoll() + { + $info = $this->sysInfo(); + $data = array( + 'CpuTotal' => $info['CpuTotal'], + 'CpuIdle' => $info['CpuIdle'], + 'MemPercent' => 100 - round((($info['MemFree'] + $info['Buffers'] + $info['Cached']) / $info['MemTotal']) * 100), + 'SwapPercent' => 100 - round(($info['SwapFree'] / $info['SwapTotal']) * 100) + ); + Header('Content-Type: application/json; charset=utf-8'); + die(json_encode($data)); + } + + protected function ajaxServices() + { + $data = array(); + + $taskId = Trigger::ldadp(); + if ($taskId === false) + return; + $status = Taskmanager::waitComplete($taskId, 10000); + + if (Taskmanager::isFailed($status)) { + if (isset($status['data']['messages'])) + $data['ldadpError'] = $status['data']['messages']; + else + $data['ldadpError'] = 'Taskmanager error'; + } + // TODO: Dozentenmodul, tftp, ... + + echo Render::parse('services', $data); + } + + protected function ajaxDmsdLog() + { + $fh = @fopen('/var/log/dmsd.log', 'r'); + if ($fh === false) { + echo 'Error opening log file'; + return; + } + fseek($fh, -6000, SEEK_END); + $data = fread($fh, 6000); + @fclose($fh); + if ($data === false) { + echo 'Error reading from log file'; + return; + } + // If we could read less, try the .1 file too + $amount = 6000 - strlen($data); + if ($amount > 100) { + $fh = @fopen('/var/log/dmsd.log.1', 'r'); + if ($fh !== false) { + fseek($fh, -$amount, SEEK_END); + $data = fread($fh, $amount) . $data; + @fclose($fh); + } + } + if (strlen($data) < 5990) { + $start = 0; + } else { + $start = strpos($data, "\n") + 1; + } + echo '<pre>', htmlspecialchars(substr($data, $start)), '</pre>'; + } + + protected function ajaxLdadpLog() + { + $files = glob('/var/log/ldadp/*.log', GLOB_NOSORT); + if ($files === false || empty($files)) echo('No logs found'); + $now = time(); + foreach ($files as $file) { + $mod = filemtime($file); + if ($now - $mod > 86400) continue; + // New enough - handle + preg_match(',/(\d+)\.log,', $file, $out); + $module = ConfigModule::get($out[1]); + if ($module === false) { + echo '<h4>Module ', $out[1], '</h4>'; + } else { + echo '<h4>Module ', htmlspecialchars($module->title()), '</h4>'; + } + $fh = @fopen($file, 'r'); + if ($fh === false) { + echo '<pre>Error opening log file</pre>'; + continue; + } + fseek($fh, -5000, SEEK_END); + $data = fread($fh, 5000); + @fclose($fh); + if ($data === false) { + echo '<pre>Error reading from log file</pre>'; + continue; + } + if (strlen($data) < 4990) { + $start = 0; + } else { + $start = strpos($data, "\n") + 1; + } + echo '<pre>', htmlspecialchars(substr($data, $start)), '</pre>'; + } + } + + protected function ajaxNetstat() + { + $taskId = Taskmanager::submit('Netstat'); + if ($taskId === false) + return; + $status = Taskmanager::waitComplete($taskId, 3500); + + if (isset($status['data']['messages'])) + $data = $status['data']['messages']; + else + $data = 'Taskmanager error'; + + echo '<pre>', htmlspecialchars($data), '</pre>'; + } + + protected function ajaxPsList() + { + $taskId = Taskmanager::submit('PsList'); + if ($taskId === false) + return; + $status = Taskmanager::waitComplete($taskId, 3500); + + if (isset($status['data']['messages'])) + $data = $status['data']['messages']; + else + $data = 'Taskmanager error'; + + echo '<pre>', htmlspecialchars($data), '</pre>'; + } + + private function usageColor($percent) + { + if ($percent <= 50) { + $r = $b = $percent / 3; + $g = (100 - $percent * (50 / 80)); + } elseif ($percent <= 70) { + $r = 55 + ($percent - 50) * (30 / 20); + $g = 60; + $b = 0; + } else { + $r = ($percent - 70) / 3 + 90; + $g = (100 - $percent) * (60 / 30); + $b = 0; + } + $r = dechex(round($r * 2.55)); + $g = dechex(round($g * 2.55)); + $b = dechex(round($b * 2.55)); + return sprintf("%02s%02s%02s", $r, $g, $b); + } + +} diff --git a/modules/systemstatus/templates/_page.html b/modules/systemstatus/templates/_page.html new file mode 100644 index 00000000..51aa5b55 --- /dev/null +++ b/modules/systemstatus/templates/_page.html @@ -0,0 +1,129 @@ +{{#rebootTask}} +<div data-tm-id="{{rebootTask}}" data-tm-log="messages">Reboot...</div> +{{/rebootTask}} + +<div class="row"> + + <div class="col-sm-6"> + <div class="panel panel-default"> + <div class="panel-heading"> + {{lang_space}} + </div> + <div class="panel-body" id="diskstat"> + <span class="glyphicon glyphicon-refresh slx-rotation"></span> + </div> + </div> + </div> + + <div class="col-sm-6"> + <div class="panel panel-default"> + <div class="panel-heading"> + {{lang_services}} + </div> + <div class="panel-body" id="services"> + <span class="glyphicon glyphicon-refresh slx-rotation"></span> + </div> + </div> + </div> + +</div> + + +<div class="row"> + + <div class="col-md-6"> + <div class="panel panel-default"> + <div class="panel-heading"> + {{lang_addressConfiguration}} + </div> + <div class="panel-body" id="addresses"> + <span class="glyphicon glyphicon-refresh slx-rotation"></span> + </div> + </div> + </div> + + <div class="col-md-6"> + <div class="panel panel-default"> + <div class="panel-heading"> + {{lang_system}} + </div> + <div class="panel-body" id="systeminfo"> + <span class="glyphicon glyphicon-refresh slx-rotation"></span> + </div> + </div> + </div> + +</div> + +<div class="row"> + + <div class="col-md-6"> + <div class="panel panel-default"> + <div class="panel-heading"> + {{lang_maintenance}} + </div> + <div class="panel-body"> + <form class="form-adduser" action="?do=SystemStatus" method="post"> + <input type="hidden" name="token" value="{{token}}"> + <input type="hidden" name="action" value="reboot"> + <div>Server Reboot</div> + <label><input type="checkbox" name="confirm" value="yep"> {{lang_iAmSure}}</label> + <button class="btn btn-warning btn-xs" type="submit">Reboot</button> + </form> + <div id="dmsd-users"></div> + </div> + </div> + </div> + +</div> + +<h3>{{lang_advanced}}</h3> + +<div class="panel panel-default"> + <div class="panel-heading"> + {{lang_dmsdLog}} + </div> + <div class="panel-body" id="dmsd-log"> + <span class="glyphicon glyphicon-refresh slx-rotation"></span> + </div> +</div> +<div class="panel panel-default"> + <div class="panel-heading"> + {{lang_ldadpLog}} + </div> + <div class="panel-body" id="ldadp-log"> + <span class="glyphicon glyphicon-refresh slx-rotation"></span> + </div> +</div> +<div class="panel panel-default"> + <div class="panel-heading"> + netstat -tulpn + </div> + <div class="panel-body" id="netstat"> + <span class="glyphicon glyphicon-refresh slx-rotation"></span> + </div> +</div> +<div class="panel panel-default"> + <div class="panel-heading"> + ps auxf + </div> + <div class="panel-body" id="pslist"> + <span class="glyphicon glyphicon-refresh slx-rotation"></span> + </div> +</div> + +<script type="text/javascript"> + document.addEventListener("DOMContentLoaded", function() { + $('#diskstat').load('?do=SystemStatus&action=DiskStat'); + $('#addresses').load('?do=SystemStatus&action=AddressList'); + $('#systeminfo').load('?do=SystemStatus&action=SystemInfo'); + $('#services').load('?do=SystemStatus&action=Services'); + $('#dmsd-users').load('?do=SystemStatus&action=DmsdUsers'); + setTimeout(function() { + $('#dmsd-log').load('?do=SystemStatus&action=DmsdLog'); + $('#netstat').load('?do=SystemStatus&action=Netstat'); + $('#pslist').load('?do=SystemStatus&action=PsList'); + $('#ldadp-log').load('?do=SystemStatus&action=LdadpLog'); + }, 300); + }, false); +</script> diff --git a/modules/systemstatus/templates/addresses.html b/modules/systemstatus/templates/addresses.html new file mode 100644 index 00000000..ce92c4d5 --- /dev/null +++ b/modules/systemstatus/templates/addresses.html @@ -0,0 +1,8 @@ +<table class="slx-table"> + {{#addresses}} + <tr {{#primary}} class="slx-bold" {{/primary}}> + <td>{{ip}}</td> + <td>({{iface}})</td> + </tr> + {{/addresses}} +</table>
\ No newline at end of file diff --git a/modules/systemstatus/templates/diskstat.html b/modules/systemstatus/templates/diskstat.html new file mode 100644 index 00000000..528d9792 --- /dev/null +++ b/modules/systemstatus/templates/diskstat.html @@ -0,0 +1,63 @@ +<div class="slx-storechart"> + {{#system}} + <b>{{lang_systemPartition}}</b> + <div id="circles-system"></div> + <div>{{lang_capacity}}: {{size}}</div> + <div>{{lang_free}}: {{free}}</div> + {{/system}} + {{^system}} + <b>{{lang_systemStoreError}}</b> + {{/system}} +</div> +<div class="slx-storechart"> + {{#store}} + <b>{{lang_vmStore}}</b> + <div id="circles-store"></div> + <div>{{lang_capacity}}: {{size}}</div> + <div>{{lang_free}}: {{free}}</div> + {{/store}} + {{^store}} + <b>{{lang_vmStoreError}}</b> + {{/store}} +</div> +<div class="clearfix"></div> +{{#notConfigured}} +<div>{{lang_storeNotConfigured}}</div> +{{/notConfigured}} +{{#storeMissing}} +<div>{{lang_storeMissingExpected}} {{storeMissing}}</div> +{{/storeMissing}} +{{#wrongStore}} +<div>{{lang_foundStore}} {{wrongStore}}</div> +{{/wrongStore}} +<a href="?do=VmStore">{{lang_goToStoreConf}}</a> +<script type="text/javascript"> + {{#store}} + Circles.create({ + id: 'circles-store', + radius: 60, + value: {{{percent}}}, + maxValue: 100, + width: 10, + text: function(value){return value + '%'; }, + colors: ['#D3B6C6', '#{{color}}'], + duration: 400, + wrpClass: 'circles-wrp', + textClass: 'circles-text' + }); + {{/store}} + {{#system}} + Circles.create({ + id: 'circles-system', + radius: 60, + value: {{{percent}}}, + maxValue: 100, + width: 10, + text: function(value){return value + '%'; }, + colors: ['#D3B6C6', '#{{color}}'], + duration: 400, + wrpClass: 'circles-wrp', + textClass: 'circles-text' + }); + {{/system}} +</script> diff --git a/modules/systemstatus/templates/services.html b/modules/systemstatus/templates/services.html new file mode 100644 index 00000000..6c4f0b93 --- /dev/null +++ b/modules/systemstatus/templates/services.html @@ -0,0 +1,6 @@ +{{#ldadpError}} +<pre>{{ldadpError}}</pre> +{{/ldadpError}} +{{^ldadpError}} +<div class="alert alert-success">LDAP-AD-Proxy: OK</div> +{{/ldadpError}} diff --git a/modules/systemstatus/templates/systeminfo.html b/modules/systemstatus/templates/systeminfo.html new file mode 100644 index 00000000..ed4a1532 --- /dev/null +++ b/modules/systemstatus/templates/systeminfo.html @@ -0,0 +1,115 @@ +<div> + {{lang_uptimeOS}}: {{uptime}} +</div> + +<div class="slx-storechart"> + <b>{{lang_cpuLoad}}</b> + {{#cpuLoadOk}} + <div id="circles-cpuload"></div> + <div>{{lang_average}}: {{cpuLoad}}%</div> + <div>{{lang_onlyOS}}: {{cpuSystem}}%</div> + <div>{{lang_logicCPUs}}: {{cpuCount}}</div> + {{/cpuLoadOk}} + {{^cpuLoadOk}} + {{lang_notDetermined}} + {{/cpuLoadOk}} +</div> + +<div class="slx-storechart"> + <b>{{lang_ramUsage}}</b> + {{#memTotal}} + <div id="circles-mem"></div> + <div>{{lang_total}}: {{memTotal}}</div> + <div>{{lang_free}}: {{memFree}}</div> + {{/memTotal}} + {{^memTotal}} + {{lang_notDetermined}} + {{/memTotal}} +</div> + +<div class="slx-storechart"> + <b>{{lang_swapUsage}}</b> + {{#memTotal}} + <div id="circles-swap"></div> + <div>{{lang_total}}: {{swapTotal}}</div> + <div>{{lang_occupied}}: {{swapUsed}}</div> + {{/memTotal}} + {{^memTotal}} + {{lang_notDetermined}} + {{/memTotal}} +</div> + +<div class="clearfix"></div> + +{{#swapWarning}} +<div> + <b>{{lang_attention}}</b> {{lang_swapWarning}} +</div> +{{/swapWarning}} + +<script type="text/javascript"> + {{#cpuLoadOk}} + var cpuCircle = Circles.create({ + id: 'circles-cpuload', + radius: 60, + value: {{{cpuLoad}}}, + maxValue: 100, + width: 10, + text: function(value){return value + '%'; }, + colors: ['#dbc', '#33f'], + duration: 400, + wrpClass: 'circles-wrp', + textClass: 'circles-text' + }); + var lastCpuTotal = {{CpuTotal}}; + var lastCpuIdle = {{CpuIdle}}; + var lastCpuPercent = {{cpuLoad}}; + {{/cpuLoadOk}} + {{#memTotal}} + var memCircle = Circles.create({ + id: 'circles-mem', + radius: 60, + value: {{{memPercent}}}, + maxValue: 100, + width: 10, + text: function(value){return value + '%'; }, + colors: ['#dbc', '#33f'], + duration: 400, + wrpClass: 'circles-wrp', + textClass: 'circles-text' + }); + var swapCircle = Circles.create({ + id: 'circles-swap', + radius: 60, + value: {{{swapPercent}}}, + maxValue: 100, + width: 10, + text: function(value){return value + '%'; }, + colors: ['#dbc', '#f33'], + duration: 400, + wrpClass: 'circles-wrp', + textClass: 'circles-text' + }); + {{/memTotal}} + function updateSystem() { + if (!cpuCircle && !memCircle) return; + $.post('?do=SystemStatus&action=SysPoll', { token: TOKEN }, function(data) { + if (memCircle && data.MemPercent) memCircle.update(data.MemPercent); + if (swapCircle && data.SwapPercent) swapCircle.update(data.SwapPercent); + if (cpuCircle && data.CpuIdle) { + var total = data.CpuTotal - lastCpuTotal; + var load = total - (data.CpuIdle - lastCpuIdle); + var percent = Math.round(100 * load / total); + cpuCircle.update(percent, Math.abs(percent - lastCpuPercent) < 5 ? 0 : 250); + lastCpuTotal = data.CpuTotal; + lastCpuIdle = data.CpuIdle; + lastCpuPercent = percent; + } + }, 'json').fail(function(data) { + console.log(data); + }).always(function() { + setTimeout(updateSystem, 1200); + }); + } + setTimeout(updateSystem, 1000); +</script> |