diff options
-rw-r--r-- | urpm/args.pm | 12 | ||||
-rw-r--r-- | urpmi.recover | 172 |
2 files changed, 184 insertions, 0 deletions
diff --git a/urpm/args.pm b/urpm/args.pm index ab0d7863..4bc5b425 100644 --- a/urpm/args.pm +++ b/urpm/args.pm @@ -313,6 +313,14 @@ my %options_spec = ( }, }, + 'urpmi.recover' => { + 'list=s' => \$::listdate, + 'list-all' => sub { $::listdate = -1 }, + checkpoint => \$::do_checkpoint, + 'rollback=s' => \$::rollback, + noclean => \$::noclean, + }, + ); # generate urpmf options callbacks @@ -370,6 +378,10 @@ foreach my $k ("help|h", "wget", "curl", "proxy=s", "proxy-user=s", "c", "f", "z $options_spec{'urpmi.addmedia'}{$k} = $options_spec{'urpmi.update'}{$k}; } +foreach my $k ("help|h", "version") { + $options_spec{'urpmi.recover'}{$k} = $options_spec{urpmi}{$k}; +} + sub parse_cmdline { my %args = @_; $urpm = $args{urpm}; diff --git a/urpmi.recover b/urpmi.recover new file mode 100644 index 00000000..312cbaf4 --- /dev/null +++ b/urpmi.recover @@ -0,0 +1,172 @@ +#!/usr/bin/perl + +# $Id$ + +#- Copyright (C) 2006 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; + +our $MACROS = '/etc/rpm/macros.d/urpmi.recover.macros'; +our $listdate; +our $do_checkpoint; +our $noclean; +our $rollback; + +sub usage () { + print N("urpmi.recover version %s +Copyright (C) 2006 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(" --list - list transactions since checkpoint + or since provided date/duration argument +") . N(" --list-all - list all transactions in rpmdb (long) +") . N(" --rollback - rollback until specified date, + or rollback the specified number of transactions +"); + exit(0); +} + +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'); +} + +#- option parsing + +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"); + +#- --list <date> and --list-all + +if ($listdate) { + my $listtime = -1; + if ($listdate ne -1) { + #- convert to timestamp + $listtime = date_to_tid($listdate); + } + my %tids; + + my $db = URPM::DB::open(); + $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"; + for 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) { + + my $db = URPM::DB::open(); #- to load macros + my $repackage_dir = URPM::expand("%_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); + my $unsafe_rollbacks = time(); + + #- clean up repackage directory + unless ($noclean) { + print N("Cleaning up repackage directory [%s]...\n", $repackage_dir); + my $nb = unlink grep !-d, glob("$repackage_dir/*"); + print N("%d files removed\n", $nb); + } + + #- 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 +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(); + $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; +} |