diff options
-rw-r--r-- | MANIFEST | 2 | ||||
-rw-r--r-- | Makefile.PL | 2 | ||||
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | po/Makefile | 2 | ||||
-rw-r--r-- | pod/urpmi.recover.8.pod | 110 | ||||
-rw-r--r-- | urpm/args.pm | 11 | ||||
-rw-r--r-- | urpmi.recover | 217 |
7 files changed, 347 insertions, 2 deletions
@@ -88,6 +88,7 @@ pod/urpmi.8.pod pod/urpmi.addmedia.8.pod pod/urpmi.cfg.5.pod pod/urpmi.files.5.pod +pod/urpmi.recover.8.pod pod/urpmi.removemedia.8.pod pod/urpmi.update.8.pod pod/urpmihowto.8.pod @@ -471,6 +472,7 @@ urpmf urpmi urpmi.addmedia urpmi.bash-completion +urpmi.recover urpmi.removemedia urpmi.schema urpmi.update diff --git a/Makefile.PL b/Makefile.PL index 39526c19..f2e7e5a2 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -16,7 +16,7 @@ my $with_po = 0; $with_po = 1 if grep $_ eq '--install-po', @ARGV; my $with_gui = 0; $with_gui = 1 if grep $_ eq '--install-gui', @ARGV; # All scripts, some of them go in /usr/sbin (see DESTINSTALLSBIN below) -our @bin_scripts = qw(urpmq urpmf rpm-find-leaves); +our @bin_scripts = qw(urpmq urpmf rpm-find-leaves urpmi.recover); our @sbin_scripts = qw(urpmi urpme urpmi.addmedia urpmi.update urpmi.removemedia rurpmi rurpme); if ($with_gui) { push @bin_scripts, qw(gurpmi); @@ -1,3 +1,8 @@ +Version 6.39 - + +- resurrect urpmi.recover now that the required functionality has been brought + back with rpm5. + Version 6.38 - 18 June 2010 - add --zeroconf support in urpmi.addmedia diff --git a/po/Makefile b/po/Makefile index b7c61764..a3074ef3 100644 --- a/po/Makefile +++ b/po/Makefile @@ -7,7 +7,7 @@ PGOAL = urpmi # perl files to search translatable strings in PL_FILES = ../rpm-find-leaves ../urpm.pm ../urpme ../urpmf \ ../urpmi ../urpmi.addmedia ../gurpmi ../gurpmi2 ../gurpmi.pm \ - ../urpmi.removemedia ../urpmi.update ../urpmq \ + ../urpmi.removemedia ../urpmi.update ../urpmq ../urpmi.recover \ ../urpm/*.pm ../rurpmi POFILES = $(shell ls *.po) diff --git a/pod/urpmi.recover.8.pod b/pod/urpmi.recover.8.pod new file mode 100644 index 00000000..2866798a --- /dev/null +++ b/pod/urpmi.recover.8.pod @@ -0,0 +1,110 @@ +=head1 NAME + +urpmi.recover - manages repackaging of old RPMs and rollbacks + +=head1 SYNOPSIS + + urpmi.recover --checkpoint [--noclean] + urpmi.recover --list '1 week ago' + urpmi.recover --rollback '1 hour ago' + urpmi.recover --disable [--noclean] + +=head1 DESCRIPTION + +B<urpmi.recover> is a tool to help management of RPM rollbacks. It has +three main functions: + +C<urpmi.recover --checkpoint> is used to define a point in your system +that you consider stable, and to start storing info that will enable you +to rollback installations and upgrades to this state. + +C<urpmi.recover --list> is used to list chronologically all installations +and upgrades on your system. (It has two variants, C<--list-all> and +C<--list-safe>.) + +C<urpmi.recover --rollback> is used to roll back installations and +upgrades to a previous point in the past (at most until your checkpoint.) + +=head1 OPTIONS + +=over 4 + +=item --checkpoint + +Define the repackaging checkpoint. From now on, using rpm and/or +urpmi/urpme to install, upgrade or remove packages, the older packages +will be stored in F</var/spool/repackage>, or whatever directory you set +the C<%_repackage_dir> rpm macro to. This way one can use them for +rollbacks. + +Technically, using this option writes a file +F</etc/rpm/macros.d/urpmi.recover.macros> that overrides the rpm macros +used to set up the repackaging functionalities of rpm. You can change +C<%_repackage_dir> there if you want to. Note that you'll probably need +plenty of space to store repackaged rpms for a long timeframe. + +You can also choose to turn off repackaging by setting +C<%_repackage_all_erasures> to 0 in this file. (Of course if you do so +rollbacks won't be possible anymore.) + +=item --noclean + +C<--checkpoint> defines a new checkpoint and removes everything in the +repackage directory. To prevent this cleaning, use the C<--noclean> +option. + +=item --list <date> + +Lists all installations and upgrades from now since the provided date, +grouped by installation transactions. The date parser is quite elaborated, +so you can give a date in ISO format or close to it (C<YYYY-MM-DD +hh:mm:ss>) or a duration (e.g. "1 day ago"). + +=item --list-all + +Lists all installations and upgrades known to the RPM database. + +=item --list-safe + +Lists all installations and upgrades up to the date of the checkpoint. + +=item --rollback <date> + +=item --rollback <number of transactions> + +Roll back the system to the given date (see C<--list> for accepted date +formats), or rolls back the given number of transactions. + +=item B<--urpmi-root> I<directory> + +Use the file system tree rooted for urpmi database and rpm install. Contrary +to B<--root>, the urpmi configuration comes from the rooted tree. + +=item --disable + +Turn off repackaging. Unless C<--noclean> was also specified, this cleans +up the repackage directory as well. To turn it on again, use +C<--checkpoint>. + +=back + +=head1 BUGS + +When enabled, you can't install and repackage delta rpms (rpms generated +with the C<makedeltarpm> tool.) Also, if you install a delta rpm, you +won't be able to rollback past this point. A sound advice would be to +completely avoid delta rpms if you're planning to use urpmi.recover. + +=head1 FILES + + /etc/rpm/macros.d/urpmi.recover.macros + +=head1 AUTHOR + +Rafael Garcia-Suarez, + +Copyright (C) 2006 Mandriva SA + +=head1 SEE ALSO + +urpmi(8), urpme(8) diff --git a/urpm/args.pm b/urpm/args.pm index d5e584cc..76229cb7 100644 --- a/urpm/args.pm +++ b/urpm/args.pm @@ -351,6 +351,17 @@ my %options_spec = ( nopubkey => \$options{nopubkey}, raw => \$options{raw}, }, + + 'urpmi.recover' => { + 'list=s' => \$::listdate, + 'list-all' => sub { $::listdate = -1 }, + 'list-safe' => sub { $::listdate = 'checkpoint' }, + checkpoint => \$::do_checkpoint, + 'rollback=s' => \$::rollback, + noclean => \$::noclean, + disable => \$::disable, + }, + ); eval diff --git a/urpmi.recover b/urpmi.recover new file mode 100644 index 00000000..39e417ae --- /dev/null +++ b/urpmi.recover @@ -0,0 +1,217 @@ +#!/usr/bin/perl + +# $Id$ + +#- Copyright (C) 2006, 2007, 2008, 2009, 2010 Mandriva SA + +use strict; + +BEGIN { + #- clean environment + $ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin"; + delete @ENV{qw(ENV BASH_ENV IFS CDPATH)}; +} + +use urpm::args; +use urpm::msg; +use URPM; +use urpm; + +our $MACROS = '/etc/rpm/macros.d/urpmi.recover.macros'; +our $listdate; +our $do_checkpoint; +our $noclean; +our $rollback; +our $disable; + +sub usage () { + print N("urpmi.recover version %s +Copyright (C) 2006-2010 Mandriva. +This is free software and may be redistributed under the terms of the GNU GPL. + +usage: +", $urpm::VERSION) . N(" --help - print this help message. +") . N(" --checkpoint - set repackaging start now +") . N(" --noclean - don't clean repackage directory on checkpoint +") . N(" --urpmi-root - use another root for urpmi db & rpm installation. +") . N(" --list - list transactions since provided date/duration argument +") . N(" --list-all - list all transactions in rpmdb (long) +") . N(" --list-safe - list transactions since checkpoint +") . N(" --rollback - rollback until specified date, + or rollback the specified number of transactions +") . N(" --disable - turn off repackaging +"); + exit(1); +} + +sub fmt_tid { + my ($tid) = @_; + require POSIX; POSIX->import('strftime'); + strftime("%F %T", localtime($tid)); +} + +sub date_to_tid { + my ($date) = @_; + require Date::Manip; Date::Manip->import; + my $d = ParseDate($date) + or die N("Invalid date or duration [%s]\n", $date); + UnixDate($d, '%s'); +} + +#- clean up repackage directory +sub clean_repackage_dir { + my ($repackage_dir) = @_; + if (!$repackage_dir || $repackage_dir eq '/') { + die N("Repackage directory not defined\n"); + } + -d $repackage_dir + or die N("Can't write to repackage directory [%s]\n", $repackage_dir); + unless ($noclean) { + print N("Cleaning up repackage directory [%s]...\n", $repackage_dir); + my $nb = unlink grep { ! -d $_ } glob("$repackage_dir/*"); + print P("%d file removed\n", "%d files removed\n", $nb, $nb); + } +} + +#- option parsing + +@ARGV or usage(); +my $command_line = "@ARGV"; #- for logging +urpm::args::parse_cmdline() + or exit(1); +@ARGV and die N("Spurious command-line arguments [%s]\n", "@ARGV"); +$do_checkpoint && $rollback + and die N("You can't specify --checkpoint and --rollback at the same time\n"); +$do_checkpoint && $listdate + and die N("You can't specify --checkpoint and --list at the same time\n"); +$rollback && $listdate + and die N("You can't specify --rollback and --list at the same time\n"); +$disable && ($listdate || $rollback || $do_checkpoint) + and die N("You can't specify --disable along with another option"); + +#- --list <date> and --list-all + +if ($listdate) { + my $listtime = -1; + if ($listdate eq 'checkpoint') { + URPM::read_config_files(); + $listtime = URPM::expand("%_unsafe_rollbacks"); + } elsif ($listdate ne -1) { + #- convert to timestamp + $listtime = date_to_tid($listdate); + } + my %tids; + + my $db = URPM::DB::open() or die "Can't open RPM DB\n"; + $db->traverse(sub { + my ($p) = @_; + my $tid = $p->installtid; + return if $tid < $listtime; + exists $tids{$tid} or $tids{$tid} = []; + push @{ $tids{$tid} }, scalar($p->fullname); + }); + + unless (scalar keys %tids) { + die N("No transaction found since %s\n", $listdate); + } + print "Date rpms\n"; + print "------------------- -------------------\n"; + foreach my $tid (sort { $a <=> $b } keys %tids) { + my @p = @{$tids{$tid}}; + print fmt_tid($tid), " ", shift(@p), "\n"; + while (@p) { + print " " x 20, shift(@p), "\n"; + } + } + exit(0); +} + +#- check we're running as root +$< == 0 or die N("You must be superuser to do this"); + +#- --checkpoint + +if ($do_checkpoint) { + + URPM::read_config_files(); + my $repackage_dir = URPM::expand("%_repackage_dir"); + my $unsafe_rollbacks = time(); + + clean_repackage_dir($repackage_dir); + + #- write rpm config file + print N("Writing rpm macros file [%s]...\n", $MACROS); + open my $fh, '>', $MACROS + or die "Can't open $MACROS for writing: $!\n"; + print $fh <<MACROS; +# Generated by urpmi.recover + +# Turn repackaging on +%_repackage_all_erasures 1 + +# Put repackaged rpms here (lots of space necessary) +%_repackage_dir $repackage_dir + +# Don't erase on rollback before this date (seconds since epoch) +%_unsafe_rollbacks $unsafe_rollbacks + +# Automate transaction rollbacks on upgrade failure +%_rollback_transaction_on_failure 0 +MACROS + close $fh; + + sys_log("checkpoint defined"); + exit(0); +} + +#- --rollback + +if ($rollback) { + sys_log("called with: $command_line"); + + my $tid; + if ($rollback !~ /\D/) { + #- $rollback contains a number of transactions to roll back + #- get a date from there + my %tids; + my $db = URPM::DB::open() or die "Can't open RPM DB\n"; + $db->traverse(sub { ++$tids{ $_[0]->installtid } }); + my @tids = sort { $b <=> $a } keys %tids; + $tid = $tids[$rollback - 1]; + } else { + #- $rollback contains a date, convert it to tid + $tid = date_to_tid($rollback); + } + $tid or die N("No rollback date found\n"); + + my $rollbackdate = fmt_tid($tid); + print N("Rollback until %s...\n", $rollbackdate), "\n"; + exec '/bin/rpm', '-Uvh', '--rollback', $rollbackdate; +} + +#- --disable + +if ($disable) { + print N("Disabling repackaging\n"); + + unless ($noclean) { + URPM::read_config_files(); + my $repackage_dir = URPM::expand("%_repackage_dir"); + clean_repackage_dir($repackage_dir); + } + + open my $fh, '<', $MACROS + or die "Can't open $MACROS for reading: $!\n"; + my $macrosfile = join '', <$fh>; + close $fh; + #- turn off repackaging macro + $macrosfile =~ s/_repackage_all_erasures\s+\w+/_repackage_all_erasures 0/g; + print N("Writing rpm macros file [%s]...\n", $MACROS); + open $fh, '>', $MACROS + or die "Can't open $MACROS for writing: $!\n"; + print $fh $macrosfile; + close $fh; + + sys_log("repackaging disabled"); + exit(0); +} |