db = $database; $this->gateway = $gateway; $this->notficationService = $notificationService; } /** * Create a SystemHealth * * @param int $systemHealthCategoryId * @param string $name * @param string $description * @param string $status * @param bool $resetAble * * @throws InvalidArgumentException|RuntimeException * * @return int|false Created SystemHealth-ID */ public function create( $systemHealthCategoryId, $name, $description = '', $status = '', $resetAble = false ) { if (empty($name)) { throw new RuntimeException('name must not empty.'); } if (!$this->gateway->getCategoryById($systemHealthCategoryId)) { throw new RuntimeException('Category not found.'); } if ($this->db->fetchValue( 'SELECT `id` FROM `systemhealth` WHERE `name` = :name AND `systemhealth_category_id` = :systemhealth_category_id', [ 'name' => $name, 'systemhealth_category_id' => $systemHealthCategoryId, ] )) { throw new RuntimeException(sprintf('name %s allready exists.', $name)); } $this->db->perform( 'INSERT INTO `systemhealth` (`id`, `systemhealth_category_id`, `name`, `description`, `status`, `created_at`, `resetable`) VALUES (NULL, :systemhealth_category_id, :name, :description, :status, NOW(), :resetable)', [ 'systemhealth_category_id' => $systemHealthCategoryId, 'name' => $name, 'description' => $description, 'status' => $status, 'resetable' => $resetAble, ] ); $insertId = (int)$this->db->lastInsertId(); if ($insertId === 0) { throw new RuntimeException('Notification message could not be created.'); } return $insertId; } /** * @param int $systemHealthId * @param string $description * @param string $status * @param bool $resetAble */ public function update($systemHealthId, $description, $status, $resetAble) { $systemHealth = $this->gateway->getById($systemHealthId); if (empty($systemHealth)) { throw new RuntimeException('SystemHealthentry not found.'); } if (!empty($status) && $status !== 'ok') { $customStatus = (string)$this->gateway->getCustomStatusLvlFromId($systemHealthId); if (!empty($customStatus)) { $status = $customStatus; } } $this->db->perform( 'UPDATE `systemhealth` SET `description` = :description, `status` = :status, `resetable` = :resetable WHERE `id` = :systemhealth_id', [ 'description' => $description, 'status' => $status, 'resetable' => $resetAble, 'systemhealth_id' => $systemHealthId, ] ); } /** * @param int $systemHealthId * @param int $userId */ public function deleteSystemHealthItemNotificationSetting($systemHealthId, $userId = 0) { $systemHealth = $this->gateway->getById($systemHealthId); if (empty($systemHealth)) { throw new RuntimeException('SystemHealthentry not found.'); } $notificationItem = $this->gateway->getItemNotificationByUserIdSystemHealthId($systemHealthId, $userId); if (empty($notificationItem)) { return; } $this->db->perform( 'DELETE FROM `systemhealth_notification_item` WHERE `id` = :id', ['id' => $notificationItem['id']] ); } /** * @param int $systemHealthId * @param int $userId */ public function createSystemHealthItemNotificationSetting($systemHealthId, $userId = 0) { $systemHealth = $this->gateway->getById($systemHealthId); if (empty($systemHealth)) { throw new RuntimeException('SystemHealthentry not found.'); } $systemHealthStatus = !empty($systemHealth['custom_status']) ? $systemHealth['custom_status'] : $systemHealth['status']; $notificationItem = $this->gateway->getItemNotificationByUserIdSystemHealthId($systemHealthId, $userId); if (empty($notificationItem)) { $this->db->perform( 'INSERT INTO `systemhealth_notification_item` (`systemhealth_id`, `user_id`, `status`) VALUES (:systemhealth_id, :user_id, :status)', [ 'systemhealth_id' => (int)$systemHealthId, 'user_id' => (int)$userId, 'status' => $systemHealthStatus, ] ); $insertId = (int)$this->db->lastInsertId(); if ($insertId === 0) { throw new RuntimeException('SystemHealthCategory could not be created.'); } return; } if ($systemHealthStatus === $notificationItem['status']) { return; } $this->db->perform( 'UPDATE `systemhealth_notification_item` SET `status` = :status WHERE `id` = :id', ['status' => $systemHealthStatus, 'id' => $notificationItem['id']] ); } /** * @param int $systemHealthId */ public function resetStatus($systemHealthId) { $systemHealth = $this->gateway->getById($systemHealthId); if (empty($systemHealth)) { throw new RuntimeException('SystemHealthentry not found.'); } if (empty($systemHealth['resetable'])) { throw new RuntimeException('SystemHealthentry is not resetable.'); } $this->db->fetchAffected( "UPDATE `systemhealth` SET `status` = '', `message` = '', `lastupdate` = NOW(), `last_reset` = NOW() WHERE `id` = :id LIMIT 1", [ 'id' => (int)$systemHealthId, ] ); } /** * @param int $systemHealthId * @param string $status * @param string $message * * @throws RuntimeException * @return bool * */ public function setStatus($systemHealthId, $status, $message = '') { $systemHealth = $this->gateway->getById($systemHealthId); if (empty($systemHealth)) { throw new RuntimeException('SystemHealthentry not found.'); } if (!empty($status) && $status !== 'ok') { $customStatus = (string)$this->gateway->getCustomStatusLvlFromId($systemHealthId); if (!empty($customStatus)) { $status = $customStatus; } } $numRows = (int)$this->db->fetchAffected( 'UPDATE `systemhealth` SET `status` = :status, `message` = :message, `lastupdate` = NOW() WHERE `id` = :id LIMIT 1', [ 'id' => (int)$systemHealthId, 'status' => $status, 'message' => $message, ] ); $return = $numRows === 1; if (!$return) { return false; } if ($status === $systemHealth['status']) { return true; } $notifications = $this->gateway->getUserNotificationsByIdAndStatus($systemHealthId, $status); if (empty($notifications)) { return true; } foreach ($notifications as $notification) { $this->notficationService->create( $notification['user_id'], $status, $systemHealth['description'], strip_tags($message), $status === 'error', [], ['systemhealth', $notification['name']] ); } return true; } /** * Create a SystemHealthCategory * * @param string $name * @param string $description * * @throws InvalidArgumentException|RuntimeException * * @return int|false Created SystemHealthCategory-ID */ public function createCategory($name, $description = '') { if (empty($name)) { throw new RuntimeException('name must not empty.'); } if ($this->db->fetchValue( 'SELECT sc.id FROM `systemhealth_category` AS `sc` WHERE sc.`name` = :name', ['name' => (string)$name] )) { throw new RuntimeException(sprintf('name %s allready exists.', $name)); } $this->db->perform( 'INSERT INTO `systemhealth_category` (`id`, `name`, `description`, `created_at`) VALUES (NULL, :name, :description, NOW())', [ 'name' => (string)$name, 'description' => $description, ] ); $insertId = (int)$this->db->lastInsertId(); if ($insertId === 0) { throw new RuntimeException('SystemHealthCategory could not be created.'); } return $insertId; } /** * @param int $systemHealthId * * @return bool */ public function delete($systemHealthId) { $numRows = (int)$this->db->fetchAffected( 'DELETE FROM `systemhealth` WHERE `id` = :id LIMIT 1', ['id' => (int)$systemHealthId] ); return $numRows === 1; } /** * @param int $SystemHealthCategoryId * * @return bool */ public function deleteCategory($SystemHealthCategoryId) { if ($this->db->fetchValue( 'SELECT sh.id FROM `systemhealth` AS `sh` WHERE sh.systemhealth_category_id = :id LIMIT 1', ['id' => (int)$SystemHealthCategoryId] )) { throw new RuntimeException('Category is not empty.'); } $numRows = (int)$this->db->fetchAffected( 'DELETE FROM `systemhealth_category` WHERE `id` = :id LIMIT 1', ['id' => (int)$SystemHealthCategoryId] ); return $numRows === 1; } /** * @param int $systemHealthId * @param string $status * @param string $doctype * @param int $doctypeId * @param string $message * * @return int */ public function createEvent($systemHealthId, $status, $doctype, $doctypeId, $message = '') { $systemHealth = $this->gateway->getById($systemHealthId); if (empty($systemHealth)) { throw new RuntimeException('Item is not empty.'); } $this->db->perform( 'INSERT INTO `systemhealth_event` (`id`, `systemhealth_id`, `created_at`, `doctype`, `doctype_id`, `status`, `message`) VALUES (NULL, :systemhealth_id, NOW(), :doctype, :doctype_id, :status, :message)', [ 'systemhealth_id' => (int)$systemHealthId, 'doctype' => $doctype, 'doctype_id' => (int)$doctypeId, 'status' => $status, 'message' => (string)$message, ] ); $insertId = (int)$this->db->lastInsertId(); if ($insertId === 0) { throw new RuntimeException('SystemHealthEvent could not be created.'); } return $insertId; } /** * @param string $dir * * @return false|float */ public function getDiskFree($dir = '') { if (!function_exists('disk_free_space')) { throw new RuntimeException('function disk_free_space not found'); } if (empty($dir)) { return disk_free_space(dirname(__DIR__, 4)); } if (!is_dir($dir)) { throw new InvalidArgumentException(sprintf('%s is no valid directory', $dir)); } return disk_free_space($dir); } /** * @param string $database * * @return array */ public function getTableSizes($database) { if (!is_string($database) || empty($database) || strpos($database, '`')) { throw new InvalidArgumentException(sprintf('%s is no valid database', $database)); } return $this->db->fetchPairs( 'SELECT table_name AS `Table`, ROUND((data_length + index_length) / 1024) AS `KB` FROM information_schema.TABLES WHERE table_schema = :database AND NOT ISNULL(data_length) ORDER BY data_length + index_length DESC', ['database' => $database] ); } /** * @param string $database * * @return int */ public function getDbSize($database) { $sizes = $this->getTableSizes($database); $ret = 0; foreach ($sizes as $size) { $ret += (int)$size; } return $ret; } /** * @return array */ public function getSystemLoad() { if (!function_exists('exec')) { throw new RuntimeException('function exec not found'); } @exec('cat /proc/loadavg 2>/dev/null ', $output, $return); if (empty($output[0])) { return [null, null, null, null, null]; } $output = reset($output); while (strpos($output, ' ') !== false) { $output = str_replace(' ', ' ', $output); } $output = explode(' ', $output); $runable = null; $threads = null; if (!empty($output[3]) && strpos($output[3], '/') !== false) { [$runable, $threads] = explode('/', $output[3]); } return [ 'act' => $output[0], '5min' => isset($output[1]) ? $output[1] : null, '15min' => isset($output[2]) ? $output[2] : null, 'runable' => $runable, 'threads' => $threads, ]; } /** * @return array */ public function getMemoryUsage() { if (!function_exists('exec')) { throw new RuntimeException('function exec not found'); } @exec('free -t -w 2>/dev/null ', $output, $return); $ret = []; if (empty($output[1])) { return []; } foreach ($output as $key => $row) { if ($key > 0) { $pos = strpos($row, ':'); $name = substr($row, 0, $pos); $row = substr($row, $pos + 1); while (strpos($row, ' ') !== false) { $row = str_replace(' ', ' ', $row); } $mems = explode(' ', trim($row)); if ($key === 1) { $type = 'memory'; } elseif ($key === count($output) - 1) { $type = 'sum'; } elseif (count($output) === 4) { $type = 'swap'; } else { continue; } $ret[$type] = [ 'name' => $name, 'sum' => $mems[0], 'used' => empty($mems[1]) ? 0 : $mems[1], 'free' => empty($mems[2]) ? 0 : $mems[2], ]; } } return $ret; } /** * @param string $dir * * @return array */ public function getPartions($dir = '') { if (!function_exists('exec')) { throw new RuntimeException('function exec not found'); } $command = 'df -B1024 -text4 -T --total 2>/dev/null'; @exec($command, $out, $return); $ret = []; $total = []; $matches = []; if (!empty($out) && count($out) > 1) { $totalKey = count($out) - 1; unset($out[0]); foreach ($out as $key => $line) { $line = str_replace(["\r", "\t", ' '], ' ', $line); while (strpos($line, ' ') !== false) { $line = str_replace(' ', ' ', $line); } $cols = explode(' ', $line); if (count($cols) < 7) { continue; } $mountPoint = $cols[6]; if ($key === $totalKey) { $total = ['total' => $cols[2], 'used' => $cols[3], 'free' => $cols[4]]; } if ($dir !== '' && strpos($dir, $mountPoint) !== 0) { unset($out[$key]); continue; } $ret[] = ['total' => $cols[2], 'used' => $cols[3], 'free' => $cols[4], 'mount' => $cols[6]]; $matches[] = strlen($cols[6]); } } if (empty($ret)) { return $total; } if (count($ret) === 1) { return reset($ret); } array_multisort($matches, SORT_DESC, $ret); return reset($ret); } /** * @param string $dir * @param string[] $excludeDirs * * @return int */ public function getUsedSpace($dir, $excludeDirs = []) { if (empty($dir) || !is_dir($dir)) { throw new InvalidArgumentException(sprintf('%s is no valid directory', $dir)); } if (!function_exists('exec')) { throw new RuntimeException('function exec not found'); } if (!empty($excludeDirs)) { foreach ($excludeDirs as $key => $excludeDir) { $excludeDir = trim($excludeDir); if (strpos($excludeDir, '..') !== false || strpos($excludeDir, "\n") !== false || strpos($excludeDir, "\r") !== false || strpos($excludeDir, "\t") !== false || strpos($excludeDir, ' ') !== false ) { unset($excludeDirs[$key]); } else { $excludeDirs[$key] = $excludeDir; } } } if (empty($excludeDirs)) { $command = sprintf( 'cd %s && du -d0 --block-size=1024 2>/dev/null', $dir ); } else { $command = sprintf( 'cd %s && du -d0 --block-size=1024 --exclude=%s 2>/dev/null', $dir, implode(' --exclude=', $excludeDirs) ); } $userdata = @exec($command, $out, $return); if (empty($userdata)) { throw new RuntimeException('failed to get Space'); } $pos = strpos($userdata, '.'); if ($pos === false) { $pos = strpos($userdata, 'total'); } if ($pos === false) { $pos = strpos($userdata, 'insgesamt'); } if ($pos === false && !empty($out)) { $userdata = trim(reset($out)); if (is_numeric($userdata)) { $pos = strlen($userdata); } } if ((int)$pos <= 0) { throw new RuntimeException('failed to get Space'); } return (int)trim(substr($userdata, 0, $pos)); } }