diff options
Diffstat (limited to 'modules-available/systemstatus/inc')
-rw-r--r-- | modules-available/systemstatus/inc/systemstatus.inc.php | 119 |
1 files changed, 113 insertions, 6 deletions
diff --git a/modules-available/systemstatus/inc/systemstatus.inc.php b/modules-available/systemstatus/inc/systemstatus.inc.php index 4413af5f..c50e0ef6 100644 --- a/modules-available/systemstatus/inc/systemstatus.inc.php +++ b/modules-available/systemstatus/inc/systemstatus.inc.php @@ -3,6 +3,8 @@ class SystemStatus { + const PROP_UPGRADABLE_COUNT = 'systemstatus.upgradable-count'; + /** * Collect status about the disk and vmstore. * The *Usage vars are filled with arrays with keys mountPoint, fileSystem, @@ -13,27 +15,28 @@ class SystemStatus * @param string|false $wantedSource What should be mounted as vmstore (false if nothing configured) * @return bool false if querying fs data from taskmanager failed */ - public static function diskStat(&$systemUsage, &$storeUsage, &$currentSource = false, &$wantedSource = false) + public static function diskStat(&$systemUsage, &$storeUsage, &$currentSource = false, &$wantedSource = false): bool { $task = Taskmanager::submit('DiskStat'); if ($task === false) return false; $task = Taskmanager::waitComplete($task, 3000); - if (!isset($task['data']['list']) || empty($task['data']['list'])) + if (empty($task['data']['list'])) return false; $wantedSource = Property::getVmStoreUrl(); - $currentSource = false; $storeUsage = false; $systemUsage = false; if ($wantedSource === '<local>') { $storePoint = '/'; + $currentSource = $wantedSource; } else { $storePoint = CONFIG_VMSTORE_DIR; + $currentSource = false; } // Collect free space information foreach ($task['data']['list'] as $entry) { - // StorePoint is either the atual directory of the vmstore, or / if we use internal storage + // StorePoint is either the actual directory of the vmstore, or / if we use internal storage if ($entry['mountPoint'] === $storePoint) { $storeUsage = $entry; } @@ -42,12 +45,116 @@ class SystemStatus $systemUsage = $entry; } // Record what's mounted at destination, regardless of config, to indicate something is wrong - if (($currentSource === false && $wantedSource === '<local>' && $entry['mountPoint'] === '/') - || $entry['mountPoint'] === CONFIG_VMSTORE_DIR) { + if ($entry['mountPoint'] === CONFIG_VMSTORE_DIR) { $currentSource = $entry['fileSystem']; + + // If internal/local storage is used but there is a mount on CONFIG_VMSTORE_DIR, + // we assume it's on purpose like a second hdd and use that + if ($wantedSource === '<local>') { + $wantedSource = $currentSource; + $storeUsage = $entry; + } } } return true; } + /** + * Get timestamp of the available updates. This is an estimate; the downloaded apt data usually + * preserves the Last-Modified timestamp from the HTTP download of the according data. Note + * that this list gets updated whenever any available package changes, so it does not necessarily + * mean that any currently installed package can be updated when the list changes. + */ + public static function getAptLastDbUpdateTime(): int + { + $osRelease = parse_ini_file('/etc/os-release'); + $updateDbTime = 0; + foreach (glob('/var/lib/apt/lists/*_dists_' . ($osRelease['VERSION_CODENAME'] ?? '') . '*_InRelease', GLOB_NOSORT) as $f) { + $b = basename($f); + if (preg_match('/dists_[a-z]+(?:[\-_](?:updates|security))?_InRelease$/', $b)) { + $updateDbTime = max($updateDbTime, filemtime($f)); + } + } + return $updateDbTime; + } + + /** + * Get timestamp when the apt database was last attempted to be updated. This does not + * imply that the operation was successful. + */ + public static function getAptLastUpdateAttemptTime(): int + { + return (int)filemtime('/var/lib/apt/lists/partial'); + } + + /** + * Get when the dpkg database was last changed, i.e. when a package was last installed, updated or removed. + * This is an estimate as it just looks at the modification time of relevant files. It is possible these + * files get modified for other reasons. + */ + public static function getDpkgLastPackageChanges(): int + { + return (int)filemtime(file_exists('/var/log/dpkg.log') ? '/var/log/dpkg.log' : '/var/lib/dpkg/status'); + } + + /** + * Get list of packages that have been updated, but require a reboot of the system + * to fully take effect. + * @return string[] + */ + public static function getPackagesRequiringReboot(): array + { + if (!file_exists('/run/reboot-required.pkgs')) + return []; + $lines = file('/run/reboot-required.pkgs', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + return array_unique($lines); + } + + /** + * Get a task struct querying upgradable packages. This adds + * a hook to the task to cache the number of upgradable packages + * that are security upgrades, so it is preferred to call this + * wrapper instead of running the task directly. + */ + public static function getUpgradableTask() + { + $task = Taskmanager::submit('AptGetUpgradable'); + if (Taskmanager::isTask($task)) { + TaskmanagerCallback::addCallback($task, 'ssUpgradable'); + } + return $task; + } + + /** + * Called from Taskmanager callback after invoking getUpgradableTask(). + */ + public static function setUpgradableData(array $task) + { + if (Taskmanager::isFailed($task) || !Taskmanager::isFinished($task) || !isset($task['data']['packages'])) + return; + $count = 0; + foreach ($task['data']['packages'] as $package) { + if (substr($package['source'], -9) === '-security') { + $count++; + } + } + error_log("Upgradable security count: $count, total count: " . count($task['data']['packages'])); + Property::set(self::PROP_UPGRADABLE_COUNT, $count . '|' . count($task['data']['packages']), 61); + } + + /** + * Get number of packages that can be upgraded and are security updates. + * This is a cached value and should be updated at least once an hour. + */ + public static function getUpgradableSecurityCount(): int + { + $str = Property::get(self::PROP_UPGRADABLE_COUNT); + if ($str === false) + return 0; + $p = explode('|', $str); + if (empty($p) || !is_numeric($p[0])) + return 0; + return (int)$p[0]; + } + }
\ No newline at end of file |