refactor: reboot type detection for downgrade via callback

This commit is contained in:
Zack Spear
2024-05-07 15:57:13 -07:00
committed by Zack Spear
parent 16db278ffd
commit 7877a5dca2
4 changed files with 113 additions and 74 deletions
@@ -14,15 +14,25 @@
* Usage:
* ```
* $rebootDetails = new RebootDetails();
* $rebootType = $rebootDetails->getRebootType();
* $rebootType = $rebootDetails->rebootType;
* ```
*/
class RebootDetails
{
/**
* @var string $rebootType Stores the type of reboot required, which can be 'update', 'downgrade', or 'thirdPartyDriversDownloading'.
*/
private $rebootType = '';
const CURRENT_CHANGES_TXT_PATH = '/boot/changes.txt';
const CURRENT_README_RELATIVE_PATH = 'plugins/unRAIDServer/README.md';
const CURRENT_VERSION_PATH = '/etc/unraid-version';
const PREVIOUS_BZ_ROOT_PATH = '/boot/previous/bzroot';
const PREVIOUS_CHANGES_TXT_PATH = '/boot/previous/changes.txt';
private $currentVersion = '';
public $rebootType = ''; // 'update', 'downgrade', 'thirdPartyDriversDownloading'
public $rebootReleaseDate = '';
public $rebootVersion = '';
public $previousReleaseDate = '';
public $previousVersion = '';
/**
* Constructs a new RebootDetails object and automatically detects the reboot type during initialization.
@@ -40,66 +50,115 @@ class RebootDetails
{
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
$rebootReadme = @file_get_contents("$docroot/plugins/unRAIDServer/README.md", false, null, 0, 20) ?: '';
/**
* Read the reboot readme, and see if it says "REBOOT REQUIRED" or "DOWNGRADE"
* only relying on the README.md file to save reads from the flash drive.
* because we started allowing downgrades from the account.unraid.net Update OS page, we can't
* fully rely on the README.md value of being accurate.
* For instance if on 6.13.0-beta.2.1 then chose to "Downgrade" to 6.13.0-beta.1.10 from the account app
* the README.md file would still say "REBOOT REQUIRED".
*/
$rebootReadme = @file_get_contents("$docroot/" . self::CURRENT_README_RELATIVE_PATH, false, null, 0, 20) ?: '';
$rebootDetected = preg_match("/^\*\*(REBOOT REQUIRED|DOWNGRADE)/", $rebootReadme);
$rebootForDowngrade = $rebootDetected && strpos($rebootReadme, 'DOWNGRADE') !== false;
$rebootForUpdate = $rebootDetected && strpos($rebootReadme, 'REBOOT REQUIRED') !== false;
$this->rebootType = $rebootForDowngrade ? 'downgrade' : ($rebootForUpdate ? 'update' : '');
if (!$rebootDetected) {
return;
}
/**
* if a reboot is required, then:
* get current Unraid version from /etc/unraid-version
* then get the version of the last update from self::CURRENT_CHANGES_TXT_PATH
* if they're different, then a reboot is required
* if the version in self::CURRENT_CHANGES_TXT_PATH is less than the current version, then a downgrade is required
* if the version in self::CURRENT_CHANGES_TXT_PATH is greater than the current version, then an update is required
*/
$this->setCurrentVersion();
$this->setRebootDetails();
if ($this->currentVersion == '' || $this->rebootVersion == '') {
return; // return to prevent potential incorrect outcome
}
// use version_compare to determine if the reboot is a downgrade or update
if (version_compare($this->rebootVersion, $this->currentVersion, '<')) {
$this->setRebootType('downgrade');
} elseif (version_compare($this->rebootVersion, $this->currentVersion, '>')) {
$this->setRebootType('update');
} elseif (version_compare($this->rebootVersion, $this->currentVersion) === 0) {
// we should never get here, but if we do, then no reboot is required and just return
return;
}
// Detect if third-party drivers were part of the update process
$processWaitingThirdPartyDrivers = "inotifywait -q /boot/changes.txt -e move_self,delete_self";
$processWaitingThirdPartyDrivers = "inotifywait -q " . self::CURRENT_CHANGES_TXT_PATH . " -e move_self,delete_self";
// Run the ps command to list processes and check if the process is running
$ps_command = "ps aux | grep -E \"$processWaitingThirdPartyDrivers\" | grep -v \"grep -E\"";
$output = shell_exec($ps_command) ?? '';
if ($this->rebootType != '' && strpos($output, $processWaitingThirdPartyDrivers) !== false) {
$this->rebootType = 'thirdPartyDriversDownloading';
$this->setRebootType('thirdPartyDriversDownloading');
}
}
/**
* Gets the type of reboot required, which can be 'update', 'downgrade', or 'thirdPartyDriversDownloading'.
*
* @return string The type of reboot required.
*/
public function getRebootType()
{
return $this->rebootType;
}
/**
* Detects and retrieves the version information related to the system reboot based on the contents of the '/boot/changes.txt' file.
*
* @return string The system version information or 'Not found' if not found, or 'File not found' if the file is not present.
*/
public function getRebootVersion()
private function readChangesTxt(string $file_path = self::CURRENT_CHANGES_TXT_PATH)
{
$file_path = '/boot/changes.txt';
// Check if the file exists
if (file_exists($file_path)) {
// Open the file for reading
$file = fopen($file_path, 'r');
// Read the file line by line until we find a line that starts with '# Version'
while (($line = fgets($file)) !== false) {
if (strpos($line, '# Version') === 0) {
// Use a regular expression to extract the full version string
if (preg_match('/# Version\s+(\S+)/', $line, $matches)) {
$fullVersion = $matches[1];
return $fullVersion;
} else {
return 'Not found';
}
exec("head -n4 $file_path", $rows);
foreach ($rows as $row) {
$i = stripos($row,'version');
if ($i !== false) {
[$version, $releaseDate] = explode(' ', trim(substr($row, $i+7)));
break;
}
}
// Close the file
fclose($file);
return [
'releaseDate' => $releaseDate ?? 'Not found',
'version' => $version ?? 'Not found',
];
} else {
return 'File not found';
}
}
/**
* Sets the current version of the unRAID server for comparison with the reboot version.
*/
private function setCurrentVersion() {
// output ex: version="6.13.0-beta.2.1"
$raw = @file_get_contents(self::CURRENT_VERSION_PATH) ?: '';
// Regular expression to match the version between the quotes
$pattern = '/version="([^"]+)"/';
if (preg_match($pattern, $raw, $matches)) {
$this->currentVersion = $matches[1];
}
}
private function setRebootDetails()
{
$rebootDetails = $this->readChangesTxt();
$this->rebootReleaseDate = $rebootDetails['releaseDate'];
$this->rebootVersion = $rebootDetails['version'];
}
private function setRebootType($rebootType)
{
$this->rebootType = $rebootType;
}
/**
* If self::PREVIOUS_BZ_ROOT_PATH exists, then the user has the option to downgrade to the previous version.
* Parse the text file /boot/previous/changes.txt to get the version number of the previous version.
* Then we move some files around and reboot.
*/
public function setPrevious()
{
if (@file_exists(self::PREVIOUS_BZ_ROOT_PATH) && @file_exists(self::PREVIOUS_CHANGES_TXT_PATH)) {
$parseOutput = $this->readChangesTxt(self::PREVIOUS_CHANGES_TXT_PATH);
$this->previousVersion = $parseOutput['version'];
$this->previousReleaseDate = $parseOutput['releaseDate'];
}
}
}
@@ -274,7 +274,7 @@ class ServerState
"osVersion" => $this->osVersion,
"osVersionBranch" => $this->osVersionBranch,
"protocol" => _var($_SERVER, 'REQUEST_SCHEME'),
"rebootType" => $this->rebootDetails->getRebootType(),
"rebootType" => $this->rebootDetails->rebootType,
"regDevs" => @(int)$this->var['regDevs'] ?? 0,
"regGen" => @(int)$this->var['regGen'],
"regGuid" => @$this->var['regGUID'] ?? '',
@@ -13,37 +13,17 @@ Tag="upload"
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
/**
* @note icon-update is rotated via CSS in myservers1.php
*/
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
// Create an instance of the RebootDetails class
$rebootDetails = new RebootDetails();
/**
* @note icon-update is rotated via CSS in myservers1.php
*
* If /boot/previous/bzroot exists, then the user has the option to downgrade to the previous version.
* Parse the text file /boot/previous/changes.txt to get the version number of the previous version.
* Then we move some files around and reboot.
*/
$restoreVersion = $restoreBranch = $restoreVersionReleaseDate = 'unknown';
$restoreExists = file_exists('/boot/previous/bzroot');
$restoreChangelogPath = '/boot/previous/changes.txt';
// Get the current reboot details if there are any
$rebootDetails->setPrevious();
$serverNameEscaped = htmlspecialchars(str_replace(' ', '_', strtolower($var['NAME'])));
if (file_exists($restoreChangelogPath)) {
exec("head -n4 $restoreChangelogPath", $rows);
foreach ($rows as $row) {
$i = stripos($row,'version');
if ($i !== false) {
[$restoreVersion, $restoreVersionReleaseDate] = explode(' ', trim(substr($row, $i+7)));
break;
}
}
$restoreBranch = strpos($restoreVersion, 'rc') !== false
? _('Next')
: (strpos($restoreVersion, 'beta') !== false
? _('Beta')
: _('Stable'));
}
?>
<script>
@@ -139,7 +119,7 @@ function startDowngrade() {
$.get(
'/plugins/dynamix.plugin.manager/include/Downgrade.php',
{
version: '<?=$restoreVersion?>',
version: '<?= $rebootDetails->previousVersion ?>',
},
function() {
refresh();
@@ -150,7 +130,7 @@ function startDowngrade() {
function confirmDowngrade() {
swal({
title: "_(Confirm Downgrade)_",
text: "<?= $restoreVersion ?><br>_(A reboot will be required)_",
text: "<?= $rebootDetails->previousVersion ?><br>_(A reboot will be required)_",
html: true,
type: 'warning',
showCancelButton: true,
@@ -167,7 +147,7 @@ function confirmDowngrade() {
<unraid-i18n-host>
<unraid-downgrade-os
reboot-version="<?= $rebootDetails->getRebootVersion() ?>"
restore-version="<?= $restoreExists && $restoreVersion != 'unknown' ? $restoreVersion : '' ?>"
restore-release-date="<?= $restoreExists && $restoreVersionReleaseDate != 'unknown' ? $restoreVersionReleaseDate : '' ?>"></unraid-downgrade-os>
reboot-version="<?= $rebootDetails->rebootVersion ?>"
restore-version="<?= $rebootDetails->previousVersion ?>"
restore-release-date="<?= $rebootDetails->previousReleaseDate ?>"></unraid-downgrade-os>
</unraid-i18n-host>
@@ -46,5 +46,5 @@ function flashBackup() {
</script>
<unraid-i18n-host>
<unraid-update-os reboot-version="<?= $rebootDetails->getRebootVersion() ?>"></unraid-update-os>
<unraid-update-os reboot-version="<?= $rebootDetails->rebootVersion ?>"></unraid-update-os>
</unraid-i18n-host>