summaryrefslogtreecommitdiffstats
path: root/modules-available/systemstatus/inc/systemstatus.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/systemstatus/inc/systemstatus.inc.php')
-rw-r--r--modules-available/systemstatus/inc/systemstatus.inc.php119
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