summaryrefslogtreecommitdiffstats
path: root/urpmi.recover
blob: fc8024a45e3af6c09e375a27ff179ee61a77d366 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#!/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;
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 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 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(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');
}

#- 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 N("%d files removed\n", $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);
}