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