3

I am running Xubuntu 24.04

A recent apt-get upgrade completely hosed my install. After spending hours troubleshooting, I eventually just reinstalled. As expected, as soon as I upgraded packages again, the same issue appeared. I'm now on my 3rd install, but if I upgrade packages, everything will be ruined again.

If/when this happens again, I want the ability to "rollback" one or more recent apt-get upgrade commands. Can this be done?

No Responses. I Guess I'm On My Own.

This is a work in progress. Here's what I've found:

  1. The info needed is in /var/log/apt/history.log
  2. Upgrades can occur during install commands as well.
  3. There may be many unattended-upgrades happening on a daily basis. I want to focus on user-initiated install and upgrade commands.

1 Answers1

3

Rollback Script

Yes, it's written in PHP. It's quick, easy, and nearly everyone is familiar with it. Here's what it does:

  1. It confirms that it's being run as 'root' (or using sudo).
  2. It builds an array of apt-get install and apt-get upgrade events found in the apt history log.
  3. It presents summaries of the 10 most recent events, and has the user select which events to roll back.
  4. If new packages were installed, then it purges the installed packages.
  5. It then goes through the upgraded packages, and downgrades them to the previously installed version (prior to the upgrade).
  6. 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";