handleServiceAction(substr($action, 8)); } if (Request::isPost()) { Util::redirect('?do=systemstatus'); } User::assertPermission('*'); } private function handleServiceAction(string $action) { $service = Request::post('service', Request::REQUIRED, 'string'); $task = Taskmanager::submit('Systemctl', ['operation' => $action, 'service' => $service]); $extra = ''; $cmp = preg_replace('/(@.*|\.service)$/', '', $service); User::assertPermission("restart.$cmp"); if ($cmp === 'dmsd') { $extra = '#id-DmsdLog_pane'; } elseif ($cmp === 'ldadp') { $extra = '#id-LdadpLog_pane'; } elseif ($cmp === 'dnbd3-server') { $extra = '#id-Dnbd3Log_pane'; } Util::redirect('?do=systemstatus&taskid=' . $task['id'] . '&taskname=' . urlencode($service) . $extra); } protected function doRender() { $data = array(); $data['taskid'] = Request::get('taskid', '', 'string'); $data['taskname'] = Request::get('taskname', 'Reboot', 'string'); $tabs = array('DmsdLog', 'Netstat', 'PsList', 'LdadpLog', 'LighttpdLog', 'Dnbd3Log'); $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') foreach ($tabs as $tab) { $data['tabs'][] = array( 'type' => $tab, 'name' => Dictionary::translate('tab_' . $tab, true), 'enabled' => User::hasPermission('tab.' . $tab), ); } 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); } 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 ajaxDiskStat() { User::assertPermission("show.overview.diskstat"); if (!SystemStatus::diskStat($systemUsage, $storeUsage, $currentSource, $wantedSource)) return; $data = ['system' => $this->convertDiskStat($systemUsage)]; if ($wantedSource === false) { // Not configured yet, nothing to display $data['notConfigured'] = true; } elseif ($wantedSource === $currentSource) { // Fine and dandy $data['store'] = $this->convertDiskStat($storeUsage); } elseif ($currentSource === false) { // No current source, nothing mounted $data['storeMissing'] = true; } else { // Something else mounted $data['wrongStore'] = $currentSource; } echo Render::parse('diskstat', $data); } protected function ajaxAddressList() { User::assertPermission("show.overview.addresses"); $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+(?\d+)\s+(?\d+)\s+(?\d+)\s+(?\d+)\s+(?\d+)\s+(?\d+)\s+(?\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() { User::assertPermission("show.overview.systeminfo"); $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'); } $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['SwapFree'] < 400000); } 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() { User::assertPermission("show.overview.systeminfo"); $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() { User::assertPermission("show.overview.services"); $data = array('services' => array()); $tasks = array(); $todo = ['dmsd', 'tftpd-hpa']; if (Module::get('dnbd3') !== false) { $todo[] = 'dnbd3-server'; $todo[] = 'dnbd3-master-proxy'; } foreach ($todo as $svc) { $tasks[] = array( 'name' => $svc, 'task' => Taskmanager::submit('Systemctl', ['service' => $svc, 'operation' => 'is-active']) ); } $ldapIds = $ldadp = false; if (Module::isAvailable('sysconfig')) { $ldapIds = ConfigModuleBaseLdap::getActiveModuleIds(); if (!empty($ldapIds)) { $ldadp = array( // No name - no display 'task' => ConfigModuleBaseLdap::ldadp('check', $ldapIds) // TODO: Proper --check usage ); $tasks[] =& $ldadp; } } $deadline = time() + 10; do { $done = true; foreach ($tasks as &$task) { if (!is_string($task['task']) && (Taskmanager::isFailed($task['task']) || Taskmanager::isFinished($task['task']))) continue; $task['task'] = Taskmanager::waitComplete($task['task'], 100); if (!Taskmanager::isFailed($task['task']) && !Taskmanager::isFinished($task['task'])) { $done = false; } } unset($task); } while (!$done && time() < $deadline); foreach ($tasks as $task) { if (!isset($task['name'])) continue; $fail = Taskmanager::isFailed($task['task']); $entry = array( 'name' => $task['name'], 'service' => $task['name'], 'fail' => $fail, ); if ($fail) { if (!isset($task['task']['data'])) { $entry['error'] = 'Taskmanager Error'; } elseif (isset($task['task']['data']['messages'])) { $entry['error'] = $task['task']['data']['messages']; } } $data['services'][] = $entry; } if ($ldadp !== false) { //error_log(print_r($ldadp, true)); preg_match_all('/^ldadp@(\d+)\.service\s+(\S+)$/m', $ldadp['task']['data']['messages'], $out, PREG_SET_ORDER); $instances = []; foreach ($out as $instance) { $instances[$instance[1]] = $instance[2]; } foreach ($ldapIds as $id) { $status = $instances[$id] ?? 'failed'; $fail = ($status !== 'running'); $data['services'][] = [ 'name' => 'LDAP/AD Proxy #' . $id, 'service' => 'ldadp@' . $id, 'fail' => $fail, 'error' => $fail ? $status : false, ]; } } echo Render::parse('services', $data); } protected function ajaxDmsdLog() { $this->showJournal('dmsd.service', 'tab.dmsdlog'); } protected function ajaxDnbd3Log() { $this->showJournal('dnbd3-server.service', 'tab.dnbd3log'); } protected function showJournal($service, $permission) { $cmp = preg_replace('/(@.*|\.service)$/', '', $service); User::assertPermission($permission); $output = [ 'name' => $service, 'service' => $service, 'task' => Taskmanager::submit('Systemctl', ['operation' => 'journal', 'service' => $service]), 'restart_disabled' => User::hasPermission('restart.' . $cmp) ? '' : 'disabled', ]; echo Render::parse('ajax-journal', ['modules' => [$output]]); } protected function ajaxLighttpdLog() { 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; } // 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); } } if (strlen($data) < 5990) { $start = 0; } else { $start = strpos($data, "\n") + 1; } echo '
', htmlspecialchars(substr($data, $start), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '
'; } protected function ajaxLdadpLog() { User::assertPermission("tab.ldadplog"); if (!Module::isAvailable('sysconfig')) { die('SysConfig module not enabled'); } $ids = ConfigModuleBaseLdap::getActiveModuleIds(); //error_log(print_r($ids, true)); $output = []; foreach ($ids as $id) { $module = ConfigModule::get($id); if ($module === false) { $name = "#$id"; } else { $name = $module->title(); } $service = "ldadp@{$id}.service"; $output[] = [ 'name' => $name, 'service' => $service, 'task' => Taskmanager::submit('Systemctl', ['operation' => 'journal', 'service' => $service]), ]; } //error_log(print_r($output, true)); echo Render::parse('ajax-journal', ['modules' => $output]); } protected function ajaxNetstat() { User::assertPermission("tab.netstat"); $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 '
', htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '
'; } protected function ajaxPsList() { User::assertPermission("tab.pslist"); $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 '
', htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '
'; } private function convertDiskStat($stat) { 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']), ]; } 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); } }