summaryrefslogblamecommitdiffstats
path: root/modules-available/systemstatus/inc/systemstatus.inc.php
blob: c50e0ef6d59f8390e0c22901f9bcf8de214e37d9 (plain) (tree)
1
2
3
4
5
6
7




                  

                                                                      









                                                                                                            
                                                                                                                           





                                                               
                                                 

                                                          



                                                  
                                                       

                                                         
                                               


                                                           
                                                                                                                    







                                                                                                                     
                                                                          
                                                                      






                                                                                                                




                            
           


                                                                                                         







                                                                                                                                           
                                                                                                        
























                                                                                                                       












                                                                                                          














































                                                                                                                         
 
<?php

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,
	 * usedPercent, sizeKb, freeKb.
	 * @param array|false $systemUsage
	 * @param array|false $storeUsage
	 * @param string|false $currentSource What's currently mounted as vmstore
	 * @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): bool
	{
		$task = Taskmanager::submit('DiskStat');
		if ($task === false)
			return false;
		$task = Taskmanager::waitComplete($task, 3000);

		if (empty($task['data']['list']))
			return false;
		$wantedSource = Property::getVmStoreUrl();
		$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 actual directory of the vmstore, or / if we use internal storage
			if ($entry['mountPoint'] === $storePoint) {
				$storeUsage = $entry;
			}
			// Always report free space on system disk
			if ($entry['mountPoint'] === '/') {
				$systemUsage = $entry;
			}
			// Record what's mounted at destination, regardless of config, to indicate something is wrong
			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];
	}

}