summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MANIFEST2
-rw-r--r--Makefile.PL2
-rw-r--r--NEWS5
-rw-r--r--po/Makefile2
-rw-r--r--pod/urpmi.recover.8.pod110
-rw-r--r--urpm/args.pm11
-rw-r--r--urpmi.recover217
7 files changed, 347 insertions, 2 deletions
diff --git a/MANIFEST b/MANIFEST
index 45487886..7e1f4ba7 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -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);
diff --git a/NEWS b/NEWS
index 7224d8c5..5589fc16 100644
--- a/NEWS
+++ b/NEWS
@@ -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);
+}