1 |
#!/usr/bin/perl |
2 |
|
3 |
# $Id$ |
4 |
|
5 |
#- Copyright (C) 2006-2010 Mandriva SA |
6 |
|
7 |
use strict; |
8 |
|
9 |
BEGIN { |
10 |
#- clean environment |
11 |
$ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin"; |
12 |
delete @ENV{qw(ENV BASH_ENV IFS CDPATH)}; |
13 |
} |
14 |
|
15 |
use urpm::args; |
16 |
use urpm::msg; |
17 |
use URPM; |
18 |
use urpm; |
19 |
|
20 |
our $MACROS = '/etc/rpm/macros.d/urpmi.recover.macros'; |
21 |
our $listdate; |
22 |
our $do_checkpoint; |
23 |
our $noclean; |
24 |
our $rollback; |
25 |
our $disable; |
26 |
|
27 |
sub usage () { |
28 |
print N("urpmi.recover version %s |
29 |
Copyright (C) 2006-2010 Mandriva. |
30 |
This is free software and may be redistributed under the terms of the GNU GPL. |
31 |
|
32 |
usage: |
33 |
", $urpm::VERSION) . N(" --help - print this help message. |
34 |
") . N(" --checkpoint - set repackaging start now |
35 |
") . N(" --noclean - don't clean repackage directory on checkpoint |
36 |
") . N(" --urpmi-root - use another root for urpmi db & rpm installation. |
37 |
") . N(" --list - list transactions since provided date/duration argument |
38 |
") . N(" --list-all - list all transactions in rpmdb (long) |
39 |
") . N(" --list-safe - list transactions since checkpoint |
40 |
") . N(" --rollback - rollback until specified date, |
41 |
or rollback the specified number of transactions |
42 |
") . N(" --disable - turn off repackaging |
43 |
"); |
44 |
exit(1); |
45 |
} |
46 |
|
47 |
sub fmt_tid { |
48 |
my ($tid) = @_; |
49 |
require POSIX; POSIX->import('strftime'); |
50 |
strftime("%F %T", localtime($tid)); |
51 |
} |
52 |
|
53 |
sub date_to_tid { |
54 |
my ($date) = @_; |
55 |
require Date::Manip; Date::Manip->import; |
56 |
my $d = ParseDate($date) |
57 |
or die N("Invalid date or duration [%s]\n", $date); |
58 |
UnixDate($d, '%s'); |
59 |
} |
60 |
|
61 |
#- clean up repackage directory |
62 |
sub clean_repackage_dir { |
63 |
my ($repackage_dir) = @_; |
64 |
if (!$repackage_dir || $repackage_dir eq '/') { |
65 |
die N("Repackage directory not defined\n"); |
66 |
} |
67 |
-d $repackage_dir |
68 |
or die N("Can't write to repackage directory [%s]\n", $repackage_dir); |
69 |
unless ($noclean) { |
70 |
print N("Cleaning up repackage directory [%s]...\n", $repackage_dir); |
71 |
my $nb = unlink grep { ! -d $_ } glob("$repackage_dir/*"); |
72 |
print P("%d file removed\n", "%d files removed\n", $nb, $nb); |
73 |
} |
74 |
} |
75 |
|
76 |
#- option parsing |
77 |
|
78 |
@ARGV or usage(); |
79 |
my $command_line = "@ARGV"; #- for logging |
80 |
urpm::args::parse_cmdline() |
81 |
or exit(1); |
82 |
@ARGV and die N("Spurious command-line arguments [%s]\n", "@ARGV"); |
83 |
$do_checkpoint && $rollback |
84 |
and die N("You can't specify --checkpoint and --rollback at the same time\n"); |
85 |
$do_checkpoint && $listdate |
86 |
and die N("You can't specify --checkpoint and --list at the same time\n"); |
87 |
$rollback && $listdate |
88 |
and die N("You can't specify --rollback and --list at the same time\n"); |
89 |
$disable && ($listdate || $rollback || $do_checkpoint) |
90 |
and die N("You can't specify --disable along with another option"); |
91 |
|
92 |
#- --list <date> and --list-all |
93 |
|
94 |
if ($listdate) { |
95 |
my $listtime = -1; |
96 |
if ($listdate eq 'checkpoint') { |
97 |
URPM::read_config_files(); |
98 |
$listtime = URPM::expand("%_unsafe_rollbacks"); |
99 |
} elsif ($listdate ne -1) { |
100 |
#- convert to timestamp |
101 |
$listtime = date_to_tid($listdate); |
102 |
} |
103 |
my %tids; |
104 |
|
105 |
my $db = URPM::DB::open() or die "Can't open RPM DB\n"; |
106 |
$db->traverse(sub { |
107 |
my ($p) = @_; |
108 |
my $tid = $p->installtid; |
109 |
return if $tid < $listtime; |
110 |
exists $tids{$tid} or $tids{$tid} = []; |
111 |
push @{ $tids{$tid} }, scalar($p->fullname); |
112 |
}); |
113 |
|
114 |
unless (scalar keys %tids) { |
115 |
die N("No transaction found since %s\n", $listdate); |
116 |
} |
117 |
print "Date rpms\n"; |
118 |
print "------------------- -------------------\n"; |
119 |
foreach my $tid (sort { $a <=> $b } keys %tids) { |
120 |
my @p = @{$tids{$tid}}; |
121 |
print fmt_tid($tid), " ", shift(@p), "\n"; |
122 |
while (@p) { |
123 |
print " " x 20, shift(@p), "\n"; |
124 |
} |
125 |
} |
126 |
exit(0); |
127 |
} |
128 |
|
129 |
#- check we're running as root |
130 |
$< == 0 or die N("You must be superuser to do this"); |
131 |
|
132 |
#- --checkpoint |
133 |
|
134 |
if ($do_checkpoint) { |
135 |
|
136 |
URPM::read_config_files(); |
137 |
my $repackage_dir = URPM::expand("%_repackage_dir"); |
138 |
my $unsafe_rollbacks = time(); |
139 |
|
140 |
clean_repackage_dir($repackage_dir); |
141 |
|
142 |
#- write rpm config file |
143 |
print N("Writing rpm macros file [%s]...\n", $MACROS); |
144 |
open my $fh, '>', $MACROS |
145 |
or die "Can't open $MACROS for writing: $!\n"; |
146 |
print $fh <<MACROS; |
147 |
# Generated by urpmi.recover |
148 |
|
149 |
# Turn repackaging on |
150 |
%_repackage_all_erasures 1 |
151 |
|
152 |
# Put repackaged rpms here (lots of space necessary) |
153 |
%_repackage_dir $repackage_dir |
154 |
|
155 |
# Don't erase on rollback before this date (seconds since epoch) |
156 |
%_unsafe_rollbacks $unsafe_rollbacks |
157 |
|
158 |
# Automate transaction rollbacks on upgrade failure |
159 |
%_rollback_transaction_on_failure 0 |
160 |
MACROS |
161 |
close $fh; |
162 |
|
163 |
sys_log("checkpoint defined"); |
164 |
exit(0); |
165 |
} |
166 |
|
167 |
#- --rollback |
168 |
|
169 |
if ($rollback) { |
170 |
sys_log("called with: $command_line"); |
171 |
|
172 |
my $tid; |
173 |
if ($rollback !~ /\D/) { |
174 |
#- $rollback contains a number of transactions to roll back |
175 |
#- get a date from there |
176 |
my %tids; |
177 |
my $db = URPM::DB::open() or die "Can't open RPM DB\n"; |
178 |
$db->traverse(sub { ++$tids{ $_[0]->installtid } }); |
179 |
my @tids = sort { $b <=> $a } keys %tids; |
180 |
$tid = $tids[$rollback - 1]; |
181 |
} else { |
182 |
#- $rollback contains a date, convert it to tid |
183 |
$tid = date_to_tid($rollback); |
184 |
} |
185 |
$tid or die N("No rollback date found\n"); |
186 |
|
187 |
my $rollbackdate = fmt_tid($tid); |
188 |
print N("Rollback until %s...\n", $rollbackdate), "\n"; |
189 |
exec '/bin/rpm', '-Uvh', '--rollback', $rollbackdate; |
190 |
} |
191 |
|
192 |
#- --disable |
193 |
|
194 |
if ($disable) { |
195 |
print N("Disabling repackaging\n"); |
196 |
|
197 |
unless ($noclean) { |
198 |
URPM::read_config_files(); |
199 |
my $repackage_dir = URPM::expand("%_repackage_dir"); |
200 |
clean_repackage_dir($repackage_dir); |
201 |
} |
202 |
|
203 |
open my $fh, '<', $MACROS |
204 |
or die "Can't open $MACROS for reading: $!\n"; |
205 |
my $macrosfile = join '', <$fh>; |
206 |
close $fh; |
207 |
#- turn off repackaging macro |
208 |
$macrosfile =~ s/_repackage_all_erasures\s+\w+/_repackage_all_erasures 0/g; |
209 |
print N("Writing rpm macros file [%s]...\n", $MACROS); |
210 |
open $fh, '>', $MACROS |
211 |
or die "Can't open $MACROS for writing: $!\n"; |
212 |
print $fh $macrosfile; |
213 |
close $fh; |
214 |
|
215 |
sys_log("repackaging disabled"); |
216 |
exit(0); |
217 |
} |