summaryrefslogtreecommitdiffstats
path: root/modules-available/systemstatus/page.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/systemstatus/page.inc.php')
-rw-r--r--modules-available/systemstatus/page.inc.php248
1 files changed, 178 insertions, 70 deletions
diff --git a/modules-available/systemstatus/page.inc.php b/modules-available/systemstatus/page.inc.php
index 238537d8..f774c4e0 100644
--- a/modules-available/systemstatus/page.inc.php
+++ b/modules-available/systemstatus/page.inc.php
@@ -3,6 +3,8 @@
class Page_SystemStatus extends Page
{
+ const TM_UPDATE_UUID = '345-45763457-24356-234324556';
+
protected function doPreprocess()
{
User::load();
@@ -13,14 +15,47 @@ class Page_SystemStatus extends Page
}
$action = Request::post('action', false, 'string');
- if ($action === 'reboot') {
+ $aptAction = null;
+ switch ($action) {
+ case 'reboot':
User::assertPermission("serverreboot");
$task = Taskmanager::submit('Reboot');
if (Taskmanager::isTask($task)) {
Util::redirect('?do=systemstatus&taskid=' . $task['id']);
}
- } elseif ($action === 'service-start' || $action === 'service-restart') {
+ break;
+ case 'service-start':
+ case 'service-restart':
$this->handleServiceAction(substr($action, 8));
+ break;
+ case 'apt-update':
+ User::assertPermission('apt.update');
+ $aptAction = 'UPDATE';
+ break;
+ case 'apt-upgrade':
+ User::assertPermission('apt.upgrade');
+ $aptAction = 'UPGRADE';
+ break;
+ case 'apt-full-upgrade':
+ User::assertPermission('apt.upgrade');
+ $aptAction = 'FULL_UPGRADE';
+ break;
+ case 'apt-autoremove':
+ User::assertPermission('apt.autoremove');
+ $aptAction = 'AUTOREMOVE';
+ break;
+ case 'apt-fix':
+ User::assertPermission('apt.fix');
+ $aptAction = 'FIX';
+ break;
+ default:
+ }
+ if ($aptAction !== null) {
+ if (!Taskmanager::isRunning(Taskmanager::status(self::TM_UPDATE_UUID))) {
+ $task = Taskmanager::submit('AptUpgrade', ['mode' => $aptAction, 'id' => self::TM_UPDATE_UUID]);
+ Taskmanager::release($task);
+ }
+ Util::redirect('?do=systemstatus#id-ListUpgradable_pane');
}
if (Request::isPost()) {
Util::redirect('?do=systemstatus');
@@ -50,22 +85,24 @@ class Page_SystemStatus extends Page
$data = array();
$data['taskid'] = Request::get('taskid', '', 'string');
$data['taskname'] = Request::get('taskname', 'Reboot', 'string');
- $tabs = array('DmsdLog', 'Netstat', 'PsList', 'LdadpLog', 'LighttpdLog', 'Dnbd3Log');
+ $tabs = ['DmsdLog', 'Netstat', 'PsList', 'LdadpLog', 'LighttpdLog', 'Dnbd3Log', 'ListUpgradable'];
$data['tabs'] = array();
// Dictionary::translate('tab_DmsdLog') Dictionary::translate('tab_LdadpLog') Dictionary::translate('tab_Netstat')
// Dictionary::translate('tab_LighttpdLog') Dictionary::translate('tab_PsList') Dictionary::translate('tab_Dnbd3Log')
+ // Dictionary::translate('tab_ListUpgradable')
foreach ($tabs as $tab) {
$data['tabs'][] = array(
'type' => $tab,
- 'name' => Dictionary::translate('tab_' . $tab, true),
+ 'name' => Dictionary::translate('tab_' . $tab),
'enabled' => User::hasPermission('tab.' . $tab),
+ 'important' => $tab === 'ListUpgradable'
+ && (SystemStatus::getAptLastDbUpdateTime() + 864000 < time() || SystemStatus::getUpgradableSecurityCount() > 0),
);
}
Permission::addGlobalTags($data['perms'], null, ['serverreboot']);
- if (file_exists('/run/reboot-required.pkgs')) {
- $lines = file('/run/reboot-required.pkgs', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
- $lines = array_unique($lines);
- $data['packages'] = implode(', ', $lines);
+ $pkgs = SystemStatus::getPackagesRequiringReboot();
+ if (!empty($pkgs)) {
+ $data['packages'] = implode(', ', $pkgs);
}
Render::addTemplate('_page', $data);
}
@@ -82,20 +119,78 @@ class Page_SystemStatus extends Page
$this->$action();
Message::renderList();
} else {
- echo "Action $action not known in " . get_class();
+ // get_class() !== get_class($this)
+ echo "Action $action not known in " . get_class($this);
}
}
+ protected function ajaxListUpgradable()
+ {
+ User::assertPermission("tab.listupgradable");
+
+ if (User::hasPermission('apt.update')
+ && Taskmanager::isRunning(Taskmanager::status(self::TM_UPDATE_UUID))) {
+ echo Render::parse('sys-update-update', [
+ 'taskid' => self::TM_UPDATE_UUID,
+ 'rnd' => mt_rand(),
+ ]);
+ return;
+ }
+
+ $task = SystemStatus::getUpgradableTask();
+
+ // Estimate last time package list was updated
+ $lastPackageInstalled = SystemStatus::getDpkgLastPackageChanges();
+ $lastListDownloadAttempt = SystemStatus::getAptLastUpdateAttemptTime();
+ $updateDbTime = SystemStatus::getAptLastDbUpdateTime();
+
+ $perms = [];
+ Permission::addGlobalTags($perms, 0, ['apt.update', 'apt.upgrade', 'apt.autoremove', 'apt.fix']);
+
+ if ($task !== false) {
+ $task = Taskmanager::waitComplete($task, 30000);
+
+ if (Taskmanager::isFailed($task) || !Taskmanager::isFinished($task)) {
+ Taskmanager::addErrorMessage($task);
+ return;
+ }
+ if (!Taskmanager::isFailed($task) && empty($task['data']['packages'])) {
+ $task['data']['error'] = '';
+ }
+ } else {
+ $task['data']['error'] = 'ECONNREFUSED';
+ }
+
+ foreach ($task['data']['packages'] as &$pkg) {
+ if (substr($pkg['source'], -9) === '-security') {
+ $pkg['row_class'] = 'bg-danger';
+ } else {
+ $pkg['row_class'] = '';
+ }
+ }
+ unset($pkg);
+
+ echo Render::parse('sys-update-main', [
+ 'task' => $task['data'],
+ 'lastDownload' => Util::prettyTime($lastListDownloadAttempt),
+ 'lastChanged' => Util::prettyTime($updateDbTime),
+ 'lastInstalled' => Util::prettyTime($lastPackageInstalled),
+ 'perm' => $perms,
+ 'list_old' => $lastListDownloadAttempt + 86400 < time(),
+ 'needReboot' => implode(', ', SystemStatus::getPackagesRequiringReboot()),
+ ]);
+ }
+
protected function ajaxDiskStat()
{
User::assertPermission("show.overview.diskstat");
if (!SystemStatus::diskStat($systemUsage, $storeUsage, $currentSource, $wantedSource))
return;
- $data = ['system' => $this->convertDiskStat($systemUsage)];
+ $data = ['system' => $this->convertDiskStat($systemUsage, 3000)];
if ($wantedSource === false) { // Not configured yet, nothing to display
$data['notConfigured'] = true;
} elseif ($wantedSource === $currentSource) { // Fine and dandy
- $data['store'] = $this->convertDiskStat($storeUsage);
+ $data['store'] = $this->convertDiskStat($storeUsage, 250000);
} elseif ($currentSource === false) { // No current source, nothing mounted
$data['storeMissing'] = true;
} else { // Something else mounted
@@ -112,7 +207,7 @@ class Page_SystemStatus extends Page
return;
$task = Taskmanager::waitComplete($task, 3000);
- if (!isset($task['data']['addresses']) || empty($task['data']['addresses'])) {
+ if (empty($task['data']['addresses'])) {
Taskmanager::addErrorMessage($task);
return;
}
@@ -129,16 +224,17 @@ class Page_SystemStatus extends Page
'addresses' => $task['data']['addresses']
));
}
-
- private function sysInfo()
+
+ private function sysInfo(): array
{
$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);
+ preg_match_all('/\b(\w+):\s+(\d+)\s/', $memInfo, $out, PREG_SET_ORDER);
foreach ($out as $e) {
$data[$e[1]] = $e[2];
}
+ /** @var array{user: numeric, nice: numeric, system: numeric, idle: numeric, iowait: numeric, irq: numeric, softirq: numeric} $out */
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'];
@@ -153,27 +249,32 @@ class Page_SystemStatus extends Page
$cpuInfo = file_get_contents('/proc/cpuinfo');
$uptime = file_get_contents('/proc/uptime');
$cpuCount = preg_match_all('/\bprocessor\s/', $cpuInfo, $out);
- //$cpuCount = count($out);
+ $out = parse_ini_file('/etc/os-release');
$data = array(
'cpuCount' => $cpuCount,
'memTotal' => '???',
'memFree' => '???',
'swapTotal' => '???',
'swapUsed' => '???',
- 'uptime' => '???'
+ 'uptime' => '???',
+ 'kernel' => php_uname('r'),
+ 'distribution' => $out['PRETTY_NAME'] ?? (($out['NAME'] ?? '???') . ' ' . ($out['VERSION'] ?? '???')),
);
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');
}
$info = $this->sysInfo();
if (isset($info['MemTotal']) && isset($info['MemFree']) && isset($info['SwapTotal'])) {
+ $avail = $info['MemAvailable'] ?? ($info['MemFree'] + $info['Buffers'] + $info['Cached']);
$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['memFree'] = Util::readableFileSize($avail * 1024);
+ $data['memPercent'] = 100 - round(($avail / $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['SwapFree'] < 400000);
+ if ($data['swapTotal'] > 0 && $data['memPercent'] > 75) {
+ $data['swapWarning'] = ($data['swapPercent'] > 80 || $info['SwapFree'] < 400000);
+ }
}
if (isset($info['CpuIdle']) && isset($info['CpuSystem']) && isset($info['CpuTotal'])) {
$data['cpuLoad'] = 100 - round(($info['CpuIdle'] / $info['CpuTotal']) * 100);
@@ -305,37 +406,38 @@ class Page_SystemStatus extends Page
echo Render::parse('ajax-journal', ['modules' => [$output]]);
}
- protected function ajaxLighttpdLog()
+ private function grepLighttpdLog(string $file, int $num): array
{
- User::assertPermission("tab.lighttpdlog");
- $fh = @fopen('/var/log/lighttpd/error.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;
+ $fh = @fopen($file, 'r');
+ if ($fh === false)
+ return ['Error opening ' . $file];
+ $ret = [];
+ fseek($fh, -($num * 2000), SEEK_END);
+ if (ftell($fh) > 0) {
+ // Throw away first line, as it's most likely incomplete
+ fgets($fh, 1000);
}
- // If we could read less, try the .1 file too
- $amount = 6000 - strlen($data);
- if ($amount > 100) {
- $fh = @fopen('/var/log/lighttpd/error.log.1', 'r');
- if ($fh !== false) {
- fseek($fh, -$amount, SEEK_END);
- $data = fread($fh, $amount) . $data;
- @fclose($fh);
+ while (($line = fgets($fh, 1000))) {
+ if (strpos($line, ':SSL routines:') === false
+ && strpos($line, ' SSL: -1 5 104 Connection reset by peer') === false
+ && strpos($line, 'GET/HEAD with content-length') === false
+ && strpos($line, 'POST-request, but content-length missing') === false) {
+ $ret[] = $line;
}
}
- if (strlen($data) < 5990) {
- $start = 0;
- } else {
- $start = strpos($data, "\n") + 1;
+ fclose($fh);
+ return array_slice($ret, -$num);
+ }
+
+ protected function ajaxLighttpdLog()
+ {
+ User::assertPermission("tab.lighttpdlog");
+ $lines = $this->grepLighttpdLog('/var/log/lighttpd/error.log', 60);
+ if (count($lines) < 50) {
+ $lines = array_merge(
+ $this->grepLighttpdLog('/var/log/lighttpd/error.log.1', 60 - count($lines)), $lines);
}
- echo '<pre>', htmlspecialchars(substr($data, $start), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '</pre>';
+ echo '<pre>', htmlspecialchars(implode('', $lines), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '</pre>';
}
protected function ajaxLdadpLog()
@@ -349,7 +451,7 @@ class Page_SystemStatus extends Page
$output = [];
foreach ($ids as $id) {
$module = ConfigModule::get($id);
- if ($module === false) {
+ if ($module === null) {
$name = "#$id";
} else {
$name = $module->title();
@@ -373,10 +475,7 @@ class Page_SystemStatus extends Page
return;
$status = Taskmanager::waitComplete($taskId, 3500);
- if (isset($status['data']['messages']))
- $data = $status['data']['messages'];
- else
- $data = 'Taskmanager error';
+ $data = $status['data']['messages'] ?? 'Taskmanager error';
echo '<pre>', htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '</pre>';
}
@@ -389,43 +488,52 @@ class Page_SystemStatus extends Page
return;
$status = Taskmanager::waitComplete($taskId, 3500);
- if (isset($status['data']['messages']))
- $data = $status['data']['messages'];
- else
- $data = 'Taskmanager error';
+ $data = $status['data']['messages'] ?? 'Taskmanager error';
echo '<pre>', htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '</pre>';
}
- private function convertDiskStat($stat)
+ /**
+ * @return array{percent: numeric, size: string, free: string, color: string, filesystem: string}
+ */
+ private function convertDiskStat(array $stat, int $minFreeMb): array
{
- if (!is_array($stat))
- return false;
return [
'percent' => $stat['usedPercent'],
'size' => Util::readableFileSize($stat['sizeKb'] * 1024),
'free' => Util::readableFileSize($stat['freeKb'] * 1024),
- 'color' => $this->usageColor($stat['usedPercent']),
+ 'color' => $this->usageColor($stat, $minFreeMb),
+ 'filesystem' => $stat['fileSystem'],
];
}
- private function usageColor($percent)
+ private function usageColor(array $stat, int $minFreeMb): string
{
- if ($percent <= 50) {
- $r = $b = $percent / 3;
- $g = (100 - $percent * (50 / 80));
- } elseif ($percent <= 70) {
- $r = 55 + ($percent - 50) * (30 / 20);
+ $freeMb = round($stat['freeKb'] / 1024);
+ // All good is half space free, or 4x the min free amount, whatever is more
+ $okFreeMb = max($minFreeMb * 4, round($stat['sizeKb']) / (1024 * 2));
+ if ($freeMb > $okFreeMb) {
+ $usedPercent = 0;
+ } elseif ($freeMb < $minFreeMb) {
+ $usedPercent = 100;
+ } else {
+ $usedPercent = 100 - round(($freeMb - $minFreeMb) / ($okFreeMb - $minFreeMb) * 100);
+ }
+ if ($usedPercent <= 50) {
+ $r = $b = $usedPercent / 3;
+ $g = (100 - $usedPercent * (50 / 80));
+ } elseif ($usedPercent <= 70) {
+ $r = 55 + ($usedPercent - 50) * (30 / 20);
$g = 60;
$b = 0;
} else {
- $r = ($percent - 70) / 3 + 90;
- $g = (100 - $percent) * (60 / 30);
+ $r = ($usedPercent - 70) / 3 + 90;
+ $g = (100 - $usedPercent) * (60 / 30);
$b = 0;
}
- $r = dechex(round($r * 2.55));
- $g = dechex(round($g * 2.55));
- $b = dechex(round($b * 2.55));
+ $r = dechex((int)round($r * 2.55));
+ $g = dechex((int)round($g * 2.55));
+ $b = dechex((int)round($b * 2.55));
return sprintf("%02s%02s%02s", $r, $g, $b);
}