Rollback Script
Yes, it's written in PHP. It's quick, easy, and nearly everyone is familiar with it. Here's what it does:
- It confirms that it's being run as 'root' (or using sudo).
- It builds an array of
apt-get install and apt-get upgrade events found in the apt history log.
- It presents summaries of the 10 most recent events, and has the user select which events to roll back.
- If new packages were installed, then it purges the installed packages.
- It then goes through the upgraded packages, and downgrades them to the previously installed version (prior to the upgrade).
- Finally it runs apt-get
check, -f install, and autoclean, to identify and attempt to fix any problems.
If you prefer, you can simply change the exec lines to echo in order to simply print the rollback commands instead of executing them.
<?php
// Confirm running as 'root' user
exec('whoami', $output);
if (implode('', $output) != 'root')
{
echo "This script must be executed as 'root' (use sudo)\n";
exit;
}
// Get recent user-initiated upgrade events
$lines = explode("\n", file_get_contents('/var/log/apt/history.log'));
$events = [];
foreach ($lines as $line)
{
if (empty($line)) continue;
list($name, $value) = explode(': ', trim($line), 2);
if ($name == 'Start-Date')
{
$event = [$name => $value];
}
elseif ($name == 'End-Date')
{
$event[$name] = $value;
if (isset($event['Requested-By']) && isset($event['Upgrade']))
{
$events[] = $event;
}
}
else
{
$event[$name] = $value;
}
}
// Show recent events
echo "Recent upgrades:\n";
foreach (array_slice($events, -10) as $i => $e)
{
echo str_pad((string) $i, 2, ' ')
. '(' . $e['Start-Date'] . ') '
. substr($e['Commandline'], 0, 100)
. "\n";
}
// Rollback user-specified events
$input = readline('Enter IDs to roll back. Separate with commas. Empty for none: ');
$ids = preg_split("/\s*\,\s*/", $input);
if (!is_numeric($ids[0]))
{
echo "Quitting. No changes were made to your system.\n";
exit;
}
else
{
foreach ($ids as $id)
{
if (!isset($events[$id]))
{
echo "Event '{$id}' not found.\n";
}
else
{
$e = $events[$id];
// Installs can include upgrades if a dependency is already installed
if (isset($e['Install']))
{
$packages = array_reverse(explode('), ', $e['Install']));
foreach ($packages as $i => $p)
{
list($name, $versions) = explode(' (', trim($p, '), '));
$cmd = "apt-get purge {$name} --yes --force-yes";
exec($cmd);
}
}
// Downgrade to package versions as they were prior to the upgrade
$packages = array_reverse(explode('), ', $e['Upgrade']));
foreach ($packages as $i => $p)
{
list($name, $versions) = explode(' (', $p);
$versions = preg_split("/\s*\,\s*/", $versions);
$name_ver = $name . '=' . $versions[0];
$cmd = "apt-get install {$name_ver} --yes --force-yes";
exec($cmd);
}
// Re-install any packages that were removed
if (isset($e['Remove']))
{
$packages = array_reverse(explode('), ', $e['Remove']));
foreach ($packages as $i => $p)
{
list($name, $version) = explode(' (', trim($p, '), '));
$cmd = "apt-get install {$name}={$version} --yes --force-yes";
exec($cmd);
}
}
}
}
// If changes were made, look for any problems, and fix them if possible
if (isset($cmd))
{
exec("apt-get update");
exec("apt-get check");
exec("apt-get -f install");
exec("apt-get autoclean");
}
}
echo "Done\n";