Imported Upstream version 2.5.0
[debian/amanda] / server-src / amoverview.pl.in
1 #!@PERL@
2
3 # Catch for sh/csh on systems without #! ability.
4 eval '(exit $?0)' && eval 'exec @PERL@ -S $0 ${1+"$@"}'
5         & eval 'exec @PERL@ -S $0 $argv:q'
6                 if 0;
7
8 require 5.001;
9
10 use FileHandle;
11 use Getopt::Long;
12 use Carp;
13 use POSIX;
14
15 sub Usage {
16     print STDERR <<END;
17 Usage: $0 [[-config] CONFIG] [-hostwidth width] [-diskwidth width] [-skipmissed] [-last] [-num0] [-togo0] [-verbose]
18
19 This script generates to standard output an overview of the filesystems
20 dumped over time and the type of dump done on a particular day, such as
21 a full dump, or an incremental, or if the dump failed.
22
23 You may override the default configuration `@DEFAULT_CONFIG@' by using
24 the -config command line option.  On larger installations, this script
25 will take a while to run.  In this case, run it with --verbose to see
26 how far along it is.
27 END
28     exit 1;
29 }
30
31 # Default paths for this installation of Amanda.
32 my $prefix='@prefix@';
33 $prefix=$prefix;                # avoid warnings about possible typo
34 my $exec_prefix="@exec_prefix@";
35 $exec_prefix=$exec_prefix;      # ditto
36 my $libexecdir="@libexecdir@";
37 my $sbindir="@sbindir@";
38  
39 # The directory where configurations can be found.
40 my $confdir="@CONFIG_DIR@";
41
42 # The default configuration.
43 my $config="@DEFAULT_CONFIG@";
44
45 # Get the version suffix.
46 my $USE_VERSION_SUFFIXES = '@USE_VERSION_SUFFIXES@';
47 my $suf = '';
48 if ( $USE_VERSION_SUFFIXES =~ /^yes$/i ) {
49         $suf='-@VERSION@';
50 }
51
52 my $amadmin     = "$sbindir/amadmin$suf";
53
54 # overrideable defaults
55 my $opt_config          = "$config";
56 my $opt_hostwidth       = 8;
57 my $opt_diskwidth       = 20;
58 my $opt_skipmissed      = 0;
59 my $opt_last            = 0;
60 my $opt_num0            = 0;
61 my $opt_togo0           = 0;
62 my $opt_verbose         = 0;
63
64 GetOptions('config=s'           => \$opt_config,
65            'hostwidth=i'        => \$opt_hostwidth,
66            'diskwidth=i'        => \$opt_diskwidth,
67            'skipmissed'         => \$opt_skipmissed,
68            'last'               => \$opt_last,
69            'num0'               => \$opt_num0,
70            'togo0'              => \$opt_togo0,
71            'verbose'            => \$opt_verbose)
72 or Usage();
73
74 if($#ARGV == 0) {
75   $opt_config = $ARGV[0];
76 }
77 elsif($#ARGV > 0) {
78   Usage();
79 }
80
81 #untaint user input $ARGV[0]
82
83 if ($opt_config =~ /^([\w.-]+)$/) {          # $1 is untainted
84    $opt_config = $1;
85 } else {
86     die "filename '$opt_config' has invalid characters.\n";
87 }
88
89
90 -d "$confdir/$opt_config" or
91         die "$0: directory `$confdir/$opt_config' does not exist.\n";
92
93 # read disklist
94 my %disks = ();
95 $::host = '';
96 $::disk = '';
97 $opt_verbose and
98     print STDERR "Running $amadmin $opt_config disklist\n";
99 my $dlfh = new FileHandle "$amadmin $opt_config disklist|" or
100     die "$0: error in opening `$amadmin $opt_config disklist' pipe: $!\n";
101 $/ = "";
102 while (<$dlfh>) {
103     ($host, $disk) = m/    host (.*?):.*    disk (.*?):/s;
104     next unless $host;
105     $disks{$host}{$disk}++;
106 }
107
108 $/ = "\n";
109 $dlfh->close or
110     die "$0: error in closing `$amadmin $opt_config disklist|' pipe: $!\n";
111
112 # Get backup dates
113 %::dates = ();
114 %::level = ();
115 $::level = '';
116 my ($date, $tape, $file, $status);
117 $opt_verbose and
118     print STDERR "Running $amadmin $opt_config find\n";
119 my $fh = new FileHandle "$amadmin $opt_config find|" or
120     die "$0: error in opening `$amadmin $opt_config find' pipe: $!\n";
121 <$fh>;
122 while (<$fh>) {
123     chomp;
124     next if /found Amanda directory/;
125     next if /skipping cruft directory/;
126     next if /skip-incr/;
127     ($date, $host, $disk, $level, $tape, $file, $part, $status) = split ' ', $_;
128     next if $date eq 'date';
129     next if $date eq 'Warning:';
130     next if $date eq 'Scanning';
131     next if $date eq "";
132     if ($date =~ /^\d\d\d\d-\d\d-\d\d$/) {
133         defined($level{$host}{$disk}{$date}) or
134             $level{$host}{$disk}{$date} = '';
135         $level{$host}{$disk}{$date} .= ($status eq 'OK') ? $level : 'E';
136         $dates{$date}++;
137     }
138     else {
139         print "bad date $date in $_\n";
140     }
141 }
142 $fh->close or
143     die "$0: error in closing `$amadmin $opt_config find|' pipe: $!\n";
144
145 # Process the status to arrive at a "last" status
146 if ($opt_last) {
147     for $host (sort keys %disks) {
148         for $disk (sort keys %{$disks{$host}}) {
149             $level{$host}{$disk}{"0000-LA-ST"} = '';
150             for $date (sort keys %dates) {
151                 if ($level{$host}{$disk}{$date} eq "E"
152                      && $level{$host}{$disk}{"0000-LA-ST"} =~ /^\d/ ) {
153                     $level{$host}{$disk}{"0000-LA-ST"} .= $level{$host}{$disk}{$date};
154                 } elsif ($level{$host}{$disk}{$date} eq "") {
155                     $level{$host}{$disk}{"0000-LA-ST"} =~ s/E//;
156                 } else {
157                     $level{$host}{$disk}{"0000-LA-ST"} = $level{$host}{$disk}{$date};
158                 }
159             }
160         }
161     }
162 }
163
164 # Number of level 0 backups
165 if ($opt_num0) {
166     for $host (sort keys %disks) {
167         for $disk (sort keys %{$disks{$host}}) {
168             $level{$host}{$disk}{'0000-NM-L0'} = 0;
169             for $date (sort keys %dates) {
170                 if ($level{$host}{$disk}{$date} =~ /0/) {
171                     $level{$host}{$disk}{'0000-NM-L0'} += 1;
172                 }
173             }
174         }
175     }
176 }
177
178 # Runs to the last level 0
179 if ($opt_togo0) {
180     for $host (sort keys %disks) {
181         for $disk (sort keys %{$disks{$host}}) {
182             $level{$host}{$disk}{'0000-TO-GO'} = 0;
183             my $togo=0;
184             for $date (sort keys %dates) {
185                 if ($level{$host}{$disk}{$date} =~ /0/) {
186                     $level{$host}{$disk}{'0000-TO-GO'} = $togo;
187                 }
188                 $togo++;
189             }
190         }
191     }
192 }
193
194 unless ($opt_skipmissed)
195 # touch all the dates just in case whole days were missed.
196 {
197     my ($start, $finish) = 
198         map {
199             my($y,$m,$d) = split /-/, $_;
200             POSIX::mktime(0,0,0,$d,$m-1,$y-1900);
201         } (sort keys %dates)[0,-1];
202
203     while ($start < $finish) {
204         my @l = localtime $start;
205         $dates{sprintf("%d-%02d-%02d", 1900+$l[5], $l[4]+1, $l[3])}++;
206         $start += 86400;
207     }
208 }
209
210 #Add the "last" entry    
211 $dates{"0000-LA-ST"}=1 if ($opt_last);
212
213 #Add the "Number of Level 0s" entry
214 $dates{"0000-NM-L0"}=1 if ($opt_num0);
215
216 #Add the "Runs to go" entry
217 $dates{"0000-TO-GO"}=1 if ($opt_togo0);
218
219 # make formats
220
221 my $top_format = "format TOP =\n\n" .
222     sprintf("%-0${opt_hostwidth}s %-0${opt_diskwidth}s ", '', 'date') .
223     join(' ', map((split(/-/, $_))[1], sort keys %dates)) . "\n" .
224     sprintf("%-0${opt_hostwidth}s %-0${opt_diskwidth}s ", 'host', 'disk') .
225     join(' ', map((split(/-/, $_))[2], sort keys %dates)) . "\n" .
226     "\n.\n";
227
228 my $out_format = "format STDOUT =\n" .
229     "@" . "<" x ($opt_hostwidth - 1) . ' ' .
230     "@" . "<" x ($opt_diskwidth - 1) . ' ' .
231     '@> ' x scalar(keys %dates) . "\n" .
232     join(', ', '$host', '$disk', 
233          map("substr(\$level{\$host}{\$disk}{'$_'},-2)", sort keys %dates)) . "\n" .
234     ".\n";
235
236 eval $top_format;
237 die $@ if $@;
238 $^ = 'TOP';
239 eval $out_format;
240 die $@ if $@;
241
242 for $host (sort keys %disks) {
243     for $disk (sort keys %{$disks{$host}}) {
244         write;
245     }
246 }