1#!@PERL@
2#
3
4# Run perl.
5eval '(exit $?0)' && eval 'exec @PERL@ -S $0 ${1+"$@"}'
6	 & eval 'exec @PERL@ -S $0 $argv:q'
7		if 0;
8
9use warnings;
10use lib '@amperldir@';
11use Time::Local;
12use Text::ParseWords;
13use Amanda::Util;
14use Amanda::Process;
15use Getopt::Long;
16
17delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV', 'PATH'};
18$ENV{'PATH'} = "/bin:/usr/bin:/usr/sbin:/sbin";       # force known path
19
20$confdir="@CONFIG_DIR@";
21$prefix='@prefix@';
22$prefix=$prefix;		# avoid warnings about possible typo
23$exec_prefix="@exec_prefix@";
24$exec_prefix=$exec_prefix;	# ditto
25$sbindir="@sbindir@";
26
27my $Amanda_process = Amanda::Process->new(0);
28$Amanda_process->load_ps_table();
29
30#$STATUS_STRANGE =  2;
31$STATUS_FAILED  =  4;
32$STATUS_MISSING =  8;
33$STATUS_TAPE    = 16;
34$exit_status    =  0;
35
36my $opt_summary;
37my $opt_stats;
38my $opt_dumping;
39my $opt_waitdumping;
40my $opt_waittaper;
41my $opt_dumpingtape;
42my $opt_writingtape;
43my $opt_finished;
44my $opt_failed;
45my $opt_estimate;
46my $opt_gestimate;
47my $opt_date;
48my $opt_config;
49my $opt_file;
50my $opt_locale_independent_date_format;
51
52sub usage() {
53	print "amstatus [--file amdump_file]\n";
54	print "         [--summary] [--dumping] [--waitdumping] [--waittaper]\n";
55	print "         [--dumpingtape] [--writingtape] [--finished] [--failed]\n";
56	print "         [--estimate] [--gestimate] [--stats] [--date]\n";
57	print "         [--locale-independent-date-format]\n";
58	print "         [--config] <config>\n";
59	exit 0;
60}
61
62my $o_options = "";
63
64Getopt::Long::Configure(qw{ bundling });
65GetOptions(
66    'summary'                        => \$opt_summary,
67    'stats|statistics'               => \$opt_stats,
68    'dumping|d'                      => \$opt_dumping,
69    'waitdumping|wdumping'           => \$opt_waitdumping,
70    'waittaper|wtaper'               => \$opt_waittaper,
71    'dumpingtape|dtape'              => \$opt_dumpingtape,
72    'writingtape|wtape'              => \$opt_writingtape,
73    'finished'                       => \$opt_finished,
74    'failed|error'                   => \$opt_failed,
75    'estimate'                       => \$opt_estimate,
76    'gestimate|gettingestimate'      => \$opt_gestimate,
77    'date'                           => \$opt_date,
78    'config|c:s'                     => \$opt_config,
79    'file:s'                         => \$opt_file,
80    'locale-independent-date-format' => \$opt_locale_independent_date_format,
81    'o:s'			     => sub { $o_options .= " -o $_[1]"; }
82    ) or usage();
83
84
85if( defined $opt_config ) {
86	$conf = $opt_config;
87}
88else {
89	if($#ARGV == 0 ) {
90		$conf=$ARGV[0];
91	}
92	else {
93		&usage();
94	}
95}
96
97#untaint user input $ARGV[0]
98
99if ($conf =~ /^([\w.-]+)$/) {          # $1 is untainted
100   $conf = $1;
101} else {
102    die "filename '$conf' has invalid characters.\n";
103}
104
105if ( ! -e "$confdir/$conf" ) {
106    print "Configuration directory '$confdir/$conf' doesn't exist\n";
107    exit 1;
108}
109if ( ! -d "$confdir/$conf" ) {
110    print "Configuration directory '$confdir/$conf' is not a directory\n";
111    exit 1;
112 }
113
114
115$pwd = `pwd`;
116chomp $pwd;
117chdir "$confdir/$conf";
118
119$logdir=`$sbindir/amgetconf logdir $o_options`;
120exit 1 if $? != 0;
121chomp $logdir;
122$errfile="$logdir/amdump";
123
124$nb_options = defined( $opt_summary ) +
125				  defined( $opt_stats ) +
126				  defined( $opt_dumping ) +
127				  defined( $opt_waitdumping ) +
128				  defined( $opt_waittaper ) +
129				  defined( $opt_dumpingtape ) +
130				  defined( $opt_writingtape ) +
131				  defined( $opt_finished ) +
132				  defined( $opt_estimate ) +
133				  defined( $opt_gestimate ) +
134				  defined( $opt_failed );
135
136if($nb_options == 0 ) {
137	$opt_summary     = 1;
138	$opt_stats       = 1;
139	$opt_dumping     = 1;
140	$opt_waitdumping = 1;
141	$opt_waittaper   = 1;
142	$opt_dumpingtape = 1;
143	$opt_writingtape = 1;
144	$opt_finished    = 1;
145	$opt_failed      = 1;
146	$opt_gestimate   = 1;
147	$opt_estimate    = 1;
148}
149
150$unit=`$sbindir/amgetconf displayunit $o_options`;
151chomp($unit);
152$unit =~ tr/A-Z/a-z/;
153$unitdivisor=1;
154if($unit eq 'k') {
155  $unitdivisor = 1;
156}
157elsif($unit eq 'm') {
158  $unitdivisor = 1024;
159}
160elsif($unit eq 'g') {
161  $unitdivisor = 1024*1024;
162}
163elsif($unit eq 't') {
164  $unitdivisor = 1024*1024*1024;
165}
166else {
167  $unit = 'k';
168  $unitdivisor = 1;
169}
170
171
172my $dead_run = 0;
173if( defined $opt_file) {
174	if( $opt_file =~ m,^/, ) {
175		$errfile = $opt_file;
176	} else {
177		$errfile = "$pwd/$opt_file";
178		$errfile = "$logdir/$opt_file" if ( ! (-f $errfile ));
179	}
180}
181else {
182	$errfile="$logdir/amflush" if(! (-f $errfile));
183	if (! -f $errfile) {
184		if (-f "$logdir/amflush.1" && -f "$logdir/amdump.1" &&
185		    -M "$logdir/amflush.1"  < -M "$logdir/amdump.1") {
186			$errfile="$logdir/amflush.1";
187		} else {
188			$errfile="$logdir/amdump.1";
189		}
190		$dead_run = 1;
191	}
192}
193
194open(AMDUMP,"<$errfile") || die("$errfile: $!");
195print "Using $errfile\n";
196
197my %taper_status_file;
198
199$start_degraded_mode = 0;
200
201$label = "";					# -w fodder
202$origsize = 0;					# -w fodder
203$idle_dumpers = 0;
204$status_driver = "";
205$status_taper->{0} = "Idle";
206$estimate_done = 0;
207$holding_space = 0;
208$start_time = 0;
209@dumpers_active = ();
210$nb_tape = 0;
211$ntpartition{$nb_tape} = 0;
212$ntsize{$nb_tape} = 0;
213$ntesize{$nb_tape} = 0;
214$tape_size = 0;
215$driver_finished = 0;
216$generating_schedule = 0;
217
218while($lineX = <AMDUMP>) {
219	chomp $lineX;
220	$lineX =~ s/[:\s]+$//g; #remove separator at end of line
221	next if $lineX eq "";
222	@line = &quotewords('[:\s]+', 0, $lineX);
223	next if !defined $line[0];
224
225	if($line[0] eq "amdump" || $line[0] eq "amflush") {
226		if ($line[1] eq "start" && $line[2] eq "at") {
227			$datestr = $lineX;
228			$datestr =~ s/.*start at //g;
229			if (!defined $opt_locale_independent_date_format) {
230				print "From " . $datestr . "\n";
231			}
232		} elsif($line[1] eq "datestamp") {
233			$gdatestamp = $line[2];
234			if(!defined $datestamp{$gdatestamp}) {
235				$datestamp{$gdatestamp} = 1;
236				push @datestamp, $gdatestamp;
237			}
238		} elsif($line[1] eq "starttime") {
239			$starttime=&set_starttime($line[2]);
240		} elsif($line[1] eq "starttime-locale-independent") {
241			if (defined $opt_locale_independent_date_format) {
242				printf "From " . $line[2] . " " . $line[3] . ":" . $line[4] . ":" . $line[5] . " " . $line[6] . "\n";
243			}
244		}
245		if($line[0] eq "amflush") {
246			$estimate_done=1;
247		}
248	} elsif($line[0] eq "planner") {
249		if($line[1] eq "timestamp") {
250			$gdatestamp = $line[2];
251			if(!defined $datestamp{$gdatestamp}) {
252				$datestamp{$gdatestamp} = 1;
253				push @datestamp, $gdatestamp;
254			}
255		}
256		elsif($line[1] eq "FAILED") {
257			#2:host 3:disk 4:datestamp 5:level 6:errmsg
258			$host=$line[2];
259			$partition=$line[3];
260			$datestamp=$line[4];
261			$hostpart=&make_hostpart($host,$partition,$datestamp);
262			$dump_started{$hostpart}=-1;
263			$level{$hostpart}=$line[5];
264			$error{$hostpart}="planner: " . $line[6];
265	} elsif($line[1] eq "time") {
266		if($line[3] eq "got") {
267				if($line[4] eq "result") {
268					$host = $line[7];
269					$partition = $line[9];
270					$hostpart=&make_hostpart($host,$partition,$gdatestamp);
271					$estimate{$hostpart}=1;
272					$level{$hostpart}=$line[10];
273					$line[12] =~ /(\d+)K/;
274					$esize{$hostpart}=$1 / $unitdivisor;
275					$partialestimate{$hostpart}=0;
276					$getest{$hostpart} = "";
277				} elsif($line[4] eq "partial") {
278					$host = $line[8];
279					$partition = $line[10];
280					$hostpart=&make_hostpart($host,$partition,$gdatestamp);
281					$level1 = $line[11];
282					$line[13] =~ /(-?\d+)K/;
283					$size1 = $1;
284					$level2 = $line[14];
285					$line[16] =~ /(-?\d+)K/;
286					$size2 = $1;
287					$level3 = $line[17];
288					$line[19] =~ /(-?\d+)K/;
289					$size3 = $1;
290					if($size1 > 0 || $size2 > 0 || $size3 > 0) {
291						$estimate{$hostpart}=1;
292						$level{$hostpart}=$line[11];
293						$esize{$hostpart}=$size1 / $unitdivisor;
294						$partialestimate{$hostpart}=1;
295						if($size1 > 0) { $getest{$hostpart} =~ s/:$level1://; }
296						if($size2 > 0) { $getest{$hostpart} =~ s/:$level2://; }
297						if($size3 > 0) { $getest{$hostpart} =~ s/:$level3://; }
298						if($getest{$hostpart} eq "") {$partialestimate{$hostpart}=0;}
299					}
300				}
301			} elsif($line[3] eq "getting" &&
302					  $line[4] eq "estimates" &&
303					  $line[5] eq "took") {
304				$estimate_done=1;
305			}
306		}
307	} elsif($line[0] eq "setup_estimate") {
308		$host = $line[1];
309		$partition = $line[2];
310		$hostpart=&make_hostpart($host,$partition,$gdatestamp);
311		$estimate{$hostpart}=0;
312		$level{$hostpart}=0;
313		$degr_level{$hostpart}=-1;
314		$esize{$hostpart}=0;
315		$dump_started{$hostpart}=0;
316		$dump_finished{$hostpart}=0;
317		$taper_started{$hostpart}=0;
318		$taper_finished{$hostpart}=0;
319		$partialestimate{$hostpart}=0;
320		$error{$hostpart}="";
321		if($line[7] eq "last_level") {
322			$getest{$hostpart}="";
323			$level1 = $line[15];
324			$level2 = $line[17];
325			$level3 = $line[19];
326			if($level1 != -1) { $getest{$hostpart} .= ":$level1:" };
327			if($level2 != -1) { $getest{$hostpart} .= ":$level2:" };
328			if($level3 != -1) { $getest{$hostpart} .= ":$level3:" };
329		}
330	} elsif($line[0] eq "GENERATING" &&
331				$line[1] eq "SCHEDULE") {
332		$generating_schedule=1;
333	} elsif($line[0] eq "--------") {
334		if ($generating_schedule == 1) {
335			$generating_schedule = 2;
336		} elsif ($generating_schedule == 2) {
337			$generating_schedule = 3;
338		}
339	} elsif($line[0] eq "DUMP") {
340		if($generating_schedule == 2 ) {
341			$host = $line[1];
342			$partition = $line[3];
343			$datestamp = $line[4];
344			$hostpart=&make_hostpart($host,$partition,$datestamp);
345			$level{$hostpart}=$line[6];
346			$esize=$line[14];	#compressed size
347			$esize=32 if $esize<32;
348			$esize{$hostpart}=$esize / $unitdivisor;
349			if(!defined($line[25])) {
350				$degr_level{$hostpart}=-1;
351			} else {
352				$degr_level{$hostpart}=$line[17];
353				$esize=$line[25];	#compressed size
354				$esize=32 if $esize<32;
355				$degr_size{$hostpart}=$esize / $unitdivisor;
356			}
357		}
358	} elsif($line[0] eq "FLUSH") {
359		$host = $line[1];
360		$partition = $line[2];
361		$datestamp = $line[3];
362		$level = $line[4];
363		$holding_file = $line[5];
364		$hostpart=&make_hostpart($host,$partition,$datestamp);
365		$flush{$hostpart}=0;
366		$dump_finished{$hostpart}=0;
367		$holding_file{$hostpart}=$holding_file;
368		$level{$hostpart}=$level;
369	} elsif($line[0] eq "driver") {
370		if($line[1] eq "pid") {
371			$pid = $line[2];
372			if (! $Amanda_process->process_alive($pid, "driver")) {
373				$dead_run = 1;
374			}
375		}
376		elsif($line[1] eq "start" && $line[2] eq "time") {
377			$start_time=$line[3];
378			$current_time=$line[3];
379			$dumpers_active[0]=0;
380			$dumpers_held[0]={};
381			$dumpers_active=0;
382		}
383		elsif($line[1] eq "tape" && $line[2] eq "size") {
384			$lineX =~ /^driver: start time (\S+)/;
385			$tape_size = $line[3] / $unitdivisor;
386		}
387		elsif($line[1] eq "adding" &&
388			   $line[2] eq "holding" &&
389				$line[3] eq "disk") {
390			$holding_space += $line[8];
391		}
392		elsif($line[1] eq "send-cmd" && $line[2] eq "time") {
393			#print "send-cmd: " , $line[5] . " " . $line[6] . " " . $line[7] . "\n" if defined $line[5] && defined $line[6] && defined $line[7];
394			$current_time = $line[3];
395			if($line[5] =~ /dumper\d*/) {
396				$dumper = $line[5];
397				if($line[6] eq "PORT-DUMP") {
398					#7:handle 8:port 9:maxdumps 10:host 11:amfeatures 12:disk 13:device 14:level ...
399					$host = $line[10];
400					$partition = $line[12];
401					$hostpart=&make_hostpart($host,$partition,$gdatestamp);
402					$serial=$line[7];
403					$dumper_to_serial{$line[5]} = $serial;
404					$dump_started{$hostpart}=1;
405					$dump_time{$hostpart}=$current_time;
406					$dump_finished{$hostpart}=0;
407					if(     $level{$hostpart} != $line[14] &&
408					   $degr_level{$hostpart} == $line[14]) {
409						$level{$hostpart}=$degr_level{$hostpart};
410						$esize{$hostpart}=$degr_size{$hostpart};
411					}
412					if(! defined($busy_time{$dumper})) {
413						$busy_time{$dumper}=0;
414					}
415					$running_dumper{$dumper} = $hostpart;
416					$error{$hostpart}="";
417					$taper_error{$hostpart}="";
418					$size{$hostpart} = 0;
419					$dumpers_active++;
420					if(! defined($dumpers_active[$dumpers_active])) {
421						$dumpers_active[$dumpers_active]=0;
422					}
423					if(! defined($dumpers_held[$dumpers_active])) {
424						$dumpers_held[$dumpers_active]={};
425					}
426				}
427			}
428			elsif($line[5] =~ /chunker\d*/) {
429				if($line[6] eq "PORT-WRITE") {
430					$host=$line[9];
431					$partition=$line[11];
432					$hostpart=&make_hostpart($host,$partition,$gdatestamp);
433					$serial=$line[7];
434					$chunker_to_serial{$line[5]} = $serial;
435					$serial{$serial}=$hostpart;
436					$holding_file{$hostpart}=$line[8];
437					#$chunk_started{$hostpart}=1;
438					$chunk_time{$hostpart}=$current_time;
439					#$chunk_finished{$hostpart}=0;
440					$size{$hostpart} = 0;
441				}
442				elsif($line[6] eq "CONTINUE") {
443					#7:handle 8:filename 9:chunksize 10:use
444					$serial=$line[7];
445					$hostpart=$serial{$serial};
446					if($hostpart ne "") {
447						$dump_roomq{$hostpart}=undef;
448						$error{$hostpart}="";
449					}
450				}
451			}
452			elsif($line[5] =~ /taper/) {
453				if($line[6] eq "START-TAPER") {
454					#7:name 8:timestamp
455					$worker = $line[7];
456					$gdatestamp=$line[8];
457					if(!defined $datestamp{$gdatestamp}) {
458						$datestamp{$gdatestamp} = 1;
459						push @datestamp, $gdatestamp;
460					}
461					$status_taper{$worker} = "Searching for a new tape";
462				}
463				elsif($line[6] eq "NEW-TAPE") {
464					#7:name 8:handle
465					$worker = $line[7];
466					$status_taper{$worker} = "Searching for a new tape";
467				}
468				elsif($line[6] eq "NO-NEW-TAPE") {
469					#7:name 8:handle 9:errmsg
470					$worker = $line[7];
471					$serial=$line[8];
472					$error=$line[9];
473					$status_taper{$worker} = $error;
474				}
475				elsif($line[6] eq "FILE-WRITE") {
476					#7:name 8:handle 9:filename 10:host 11:disk 12:level 13:datestamp 14:splitsize
477					$worker = $line[7];
478					$name=$line[7];
479					$serial=$line[8];
480					$host=$line[10];
481					$partition=$line[11];
482					$level=$line[12];
483					$ldatestamp=$line[13];
484					$status_taper{$worker} = "Writing $host:$partition";
485					if(!defined $datestamp{$ldatestamp}) {
486						$datestamp{$ldatestamp} = 1;
487						push @datestamp, $ldatestamp;
488					}
489					$hostpart=&make_hostpart($host,$partition,$ldatestamp);
490					$serial{$serial}=$hostpart;
491					if(!defined $level{$hostpart}) {
492						$level{$hostpart} = $level;
493					}
494					$taper_started{$hostpart}=1;
495					$taper_finished{$hostpart}=0;
496					$taper_time{$hostpart}=$current_time;
497					$taper_error{$hostpart}="";
498					$taper_name{$hostpart} = $name;
499					$worker_to_serial{$name} = $serial;
500				    $tapedsize{$hostpart} = 0;
501				}
502				elsif($line[6] eq "PORT-WRITE") {
503					#7:name 8:handle 9:host 10:disk 11:level 12:datestamp 13:splitsize 14:diskbuffer 15:fallback_splitsize
504					$name=$line[7];
505					$worker = $line[7];
506					$serial=$line[8];
507					$host=$line[9];
508					$partition=$line[10];
509					$level=$line[11];
510					$ldatestamp=$line[12];
511					$status_taper{$worker} = "Writing $host:$partition";
512					$hostpart=&make_hostpart($host,$partition,$ldatestamp);
513					$serial{$serial}=$hostpart;
514					$taper_started{$hostpart}=1;
515					$taper_finished{$hostpart}=0;
516					$taper_time{$hostpart}=$current_time;
517					$taper_error{$hostpart}="";
518					$taper_name{$hostpart} = $name;
519					$worker_to_serial{$name} = $serial;
520				    $tapedsize{$hostpart} = 0;
521					$size{$hostpart} = 0;
522				}
523				elsif($line[6] eq "TAKE-SCRIBE-FROM") {
524					#7:name1 #8:handle #9:name2
525					$worker1 = $line[7];
526					$name1=$line[7];
527					$serial=$line[8];
528					$worker2 = $line[7];
529					$name2=$line[9];
530					$hostpart=$serial{$serial};
531					$taper_nb{$name1} = $taper_nb{$name2};
532					$taper_nb{$name2} = 0;
533					if (defined $hostpart) {
534						$error{$hostpart} = $olderror{$hostpart};
535					}
536					$status_taper{$worker1} = $status_taper{$worker2};
537					$status_taper{$worker2} = "Idle";
538				}
539			}
540		}
541		elsif($line[1] eq "result" && $line[2] eq "time") {
542			#print "result: " , $line[5] . " " . $line[6] . " " . $line[7] . "\n" if defined $line[5] && defined $line[6] && defined $line[7];
543			$current_time = $line[3];
544			if($line[5] =~ /dumper\d+/) {
545				if($line[6] eq "(eof)") {
546					$line[6] = "FAILED";
547					$line[7] = $dumper_to_serial{$line[5]};
548					$line[8] = "dumper CRASH";
549				}
550				if($line[6] eq "FAILED" || $line[6] eq "TRY-AGAIN") {
551					#7:handle 8:message
552					$serial = $line[7];
553					$error = $line[8];
554					$hostpart=$serial{$serial};
555					if ($taper_started{$hostpart} == 1) {
556						$dump_finished{$hostpart}=-1;
557					} else {
558						$dump_finished{$hostpart}=-3;
559					}
560					$busy_time{$line[5]}+=($current_time-$dump_time{$hostpart});
561					$running_dumper{$line[5]} = "0";
562					$dump_time{$hostpart}=$current_time;
563					if (!$taper_error{$hostpart}) {
564						$error{$hostpart}="dumper: $error";
565					}
566					$dumpers_active--;
567
568				}
569				elsif($line[6] eq "DONE") {
570					#7:handle 8:origsize 9:size ...
571					$serial=$line[7];
572					$origsize=$line[8] / $unitdivisor;
573					$outputsize=$line[9] / $unitdivisor;
574					$hostpart=$serial{$serial};
575					$size{$hostpart}=$outputsize;
576					$dump_finished{$hostpart}=1;
577					$busy_time{$line[5]}+=($current_time-$dump_time{$hostpart});
578					$running_dumper{$line[5]} = "0";
579					$dump_time{$hostpart}=$current_time;
580					$error{$hostpart}="";
581					$dumpers_active--;
582				}
583				elsif($line[6] eq "ABORT-FINISHED") {
584					#7:handle
585					$serial=$line[7];
586					$hostpart=$serial{$serial};
587					$dump_started{$hostpart}=0;
588					if ($taper_started{$hostpart} == 1) {
589						$dump_finished{$hostpart}=-1;
590					} else {
591						$dump_finished{$hostpart}=-3;
592					}
593					$busy_time{$line[5]}+=($current_time-$dump_time{$hostpart});
594					$running_dumper{$line[5]} = "0";
595					$dump_time{$hostpart}=$current_time;
596					$error{$hostpart}="dumper: (aborted)";
597					$dumpers_active--;
598				}
599			}
600			elsif($line[5] =~ /chunker\d+/) {
601				if($line[6] eq "(eof)") {
602					$line[6] = "FAILED";
603					$line[7] = $chunker_to_serial{$line[5]};
604					$line[8] = "chunker CRASH";
605				}
606				if($line[6] eq "DONE" || $line[6] eq "PARTIAL") {
607					#7:handle 8:size
608					$serial=$line[7];
609					$outputsize=$line[8] / $unitdivisor;
610					$hostpart=$serial{$serial};
611					$size{$hostpart}=$outputsize;
612					if ($line[6] eq "DONE") {
613						$dump_finished{$hostpart}=1;
614					} else {
615						$dump_finished{$hostpart}=-3;
616					}
617					$busy_time{$line[5]}+=($current_time-$chunk_time{$hostpart});
618					$running_dumper{$line[5]} = "0";
619					$chunk_time{$hostpart}=$current_time;
620					if ($line[6] eq "PARTIAL") {
621						$partial{$hostpart} = 1;
622					}
623					else {
624						$partial{$hostpart} = 0;
625						$error{$hostpart}="";
626					}
627				}
628				elsif($line[6] eq "FAILED") {
629					$serial=$line[7];
630					$hostpart=$serial{$serial};
631					$dump_finished{$hostpart}=-1;
632					$busy_time{$line[5]}+=($current_time-$chunk_time{$hostpart});
633					$running_dumper{$line[5]} = "0";
634					$chunk_time{$hostpart}=$current_time;
635					$error{$hostpart}="chunker: " .$line[8] if $error{$hostpart} eq "";
636				}
637				elsif($line[6] eq "RQ-MORE-DISK") {
638					#7:handle
639					$serial=$line[7];
640					$hostpart=$serial{$serial};
641					$dump_roomq{$hostpart}=1;
642					$error{$hostpart}="(waiting for holding disk space)";
643				}
644			}
645			elsif($line[5] eq "taper") {
646				if($line[6] eq "(eof)") {
647					# all worker fail
648					foreach $worker (keys %worker_to_serial) {
649						$serial = $worker_to_serial{$worker};
650						$hostpart=$serial{$serial};
651						if(defined $hostpart) {
652							$error= "taper CRASH";
653							$taper_finished{$hostpart} = -2;
654							foreach $worker (keys %status_taper) {
655								$status_taper{$worker} = $error;
656							}
657							$busy_time{"taper"}+=($current_time-$taper_time{$hostpart});
658							$taper_time{$hostpart}=$current_time;
659							$error{$hostpart}="$error";
660							undef $worker_to_serial{$worker};
661						}
662						undef $taper_status_file{$hostpart};
663					}
664				}
665				elsif($line[6] eq "DONE" || $line[6] eq "PARTIAL") {
666					#DONE:    7:handle 8:label 9:filenum 10:errstr
667					#PARTIAL: 7:handle 8:INPUT-* 9:TAPE-* 10:errstr 11:INPUT-MSG 12:TAPE-MSG
668					$serial=$line[7];
669
670					$hostpart=$serial{$serial};
671					$worker = $taper_name{$hostpart};
672					$status_taper{$worker} = "Idle";
673					$line[10] =~ /sec (\S+) (kb|bytes) (\d+) kps/;
674					if ($2 eq 'kb') {
675						$size=$3 / $unitdivisor;
676					} else {
677						$size=$3 / ( $unitdivisor * 1024);
678					}
679					$taper_finished{$hostpart}=1;
680					$busy_time{"taper"}+=($current_time-$taper_time{$hostpart});
681					$taper_time{$hostpart}=$current_time;
682					if(!defined $size{$hostpart}) {
683						$size{$hostpart}=$size;
684					}
685					$ntape = $taper_nb{$taper_name{$hostpart}};
686					$ntpartition{$ntape}++ if defined $ntape;
687					if ($line[6] eq "PARTIAL") {
688						$partial{$hostpart} = 1;
689						if ($line[9] eq "TAPE-ERROR") {
690							$error{$hostpart} = "taper: $line[12]";
691							$taper_error{$hostpart} = "taper: $line[12]";
692						}
693					}
694					else {
695						$partial{$hostpart} = 0;
696					}
697					undef $taper_status_file{$hostpart};
698					undef $worker_to_serial{$taper_name{$hostpart}};
699				}
700				elsif($line[6] eq "PARTDONE") {
701					#7:handle 8:label 9:filenum 10:ksize 11:errstr
702					$serial=$line[7];
703					$hostpart=$serial{$serial};
704					#$line[11] =~ /.*kb (\d*) kps/;
705					#$size=$1 / $unitdivisor;
706					$size=$line[10] / $unitdivisor;
707					$tapedsize{$hostpart} += $size;
708					$ntape = $taper_nb{$taper_name{$hostpart}};
709					$ntchunk{$ntape}++;
710					$ntsize{$ntape} += $size;
711					$ntesize{$ntape} += $size;
712				}
713				elsif($line[6] eq "REQUEST-NEW-TAPE") {
714					#7:serial
715					$serial=$line[7];
716					$hostpart=$serial{$serial};
717					$worker = $taper_name{$hostpart};
718					$old_status_taper{$worker} = $status_taper{$worker};
719					$status_taper{$worker} = "Asking for a new tape";
720					if (defined $hostpart and
721						!defined($olderror{$hostpart})) {
722						$olderror{$hostpart} = $error{$hostpart};
723						$error{$hostpart} = "waiting for a new tape";
724					}
725				}
726				elsif($line[6] eq "NEW-TAPE") {
727					#7:serial #8:label
728					$serial=$line[7];
729					$hostpart=$serial{$serial};
730					$worker = $taper_name{$hostpart};
731					$status_taper{$worker} = $old_status_taper{$worker};
732					$nb_tape++;
733					$taper_nb{$taper_name{$hostpart}} = $nb_tape;
734					$label = $line[8];
735					$ntlabel{$nb_tape} = $label;
736					$ntpartition{$nb_tape} = 0;
737					$ntsize{$nb_tape} = 0;
738					$ntesize{$nb_tape} = 0;
739					if (defined $hostpart) {
740						$error{$hostpart} = $olderror{$hostpart};
741					}
742				}
743				elsif($line[6] eq "TAPER-OK") {
744					#7:name #8:label
745					$worker = $line[7];
746					$status_taper{$worker} = "Idle";
747				}
748				elsif($line[6] eq "TAPE-ERROR") {
749					#7:name 8:errstr
750					$worker = $line[7];
751					$error=$line[8];
752					$status_taper{$worker} = $error;
753					$exit_status |= $STATUS_TAPE;
754					undef $taper_status_file{$hostpart};
755				}
756				elsif($line[6] eq "FAILED") {
757					#7:handle 8:INPUT- 9:TAPE- 10:input_message 11:tape_message
758					$serial=$line[7];
759					$hostpart=$serial{$serial};
760					$worker = $taper_name{$hostpart};
761					if(defined $hostpart) {
762						if($line[9] eq "TAPE-ERROR") {
763							$error=$line[11];
764							$taper_finished{$hostpart} = -2;
765							$status_taper{$worker} = $error;
766						} elsif($line[9] eq "TAPE-CONFIG") {
767							$tape_config{$hostpart} = $error;
768							$error=$line[11];
769							$tape_config{$hostpart} = $error;
770							$taper_finished{$hostpart} = -2;
771							$status_taper{$worker} = $error;
772						} else { # INPUT-ERROR
773							$error = $line[10];
774							$error = $error{$hostpart} if defined $error{$hostpart};
775							$taper_finished{$hostpart} = -1;
776							$status_taper{$worker} = "Idle";
777						}
778						$busy_time{"taper"}+=($current_time-$taper_time{$hostpart});
779						$taper_time{$hostpart}=$current_time;
780						$error{$hostpart}="$error";
781					}
782					undef $taper_status_file{$hostpart};
783					undef $worker_to_serial{$taper_name{$hostpart}};
784				}
785			}
786		}
787		elsif($line[1] eq "finished-cmd" && $line[2] eq "time") {
788			$current_time=$line[3];
789			if($line[4] =~ /dumper\d+/) {
790			}
791		}
792		elsif($line[1] eq "dump" && $line[2] eq "failed") {
793			#3:handle 4: 5: 6:"too many dumper retry"
794			$serial=$line[3];
795			$hostpart=$serial{$serial};
796			$dump_started{$hostpart}=-1;
797			$dump_finished{$hostpart}=-2;
798			$error{$hostpart} .= "(" . $line[6] . ")";
799		}
800		elsif($line[1] eq "tape" && $line[2] eq "failed") {
801			#3:handle 4: 5: 6:"too many dumper retry"
802			$serial=$line[3];
803			$hostpart=$serial{$serial};
804			$taper_started{$hostpart}=-1;
805			$taper_finished{$hostpart}=-2;
806			$error{$hostpart} .= "(" . $line[6] . ")";
807		}
808		elsif($line[1] eq "state" && $line[2] eq "time") {
809			#3:time 4:"free" 5:"kps" 6:free 7:"space" 8:space 9:"taper" 10:taper 11:"idle-dumpers" 12:idle-dumpers 13:"qlen" 14:"tapeq" 15:tapeq 16:"runq" 17:runq 18:"roomq" 19:roomq 20:"wakeup" 21:wakeup 22:"driver-idle" 23:driver-idle
810			$current_time=$line[3];
811			$idle_dumpers=$line[12];
812
813			$free{"kps"} = $line[6];
814			$free{"space"} = $line[8];
815			$qlen{"tapeq"} = $line[15];
816			$qlen{"runq"} = $line[17];
817			$qlen{"roomq"} = $line[19];
818
819			if(defined($dumpers_active)) {
820				if($status_driver ne "") {
821					$dumpers_active[$dumpers_active_prev]
822						+=$current_time-$state_time_prev;
823					$dumpers_held[$dumpers_active_prev]{$status_driver}
824						+=$current_time-$state_time_prev;
825				}
826				$state_time_prev=$current_time;
827				$dumpers_active_prev=$dumpers_active;
828				$status_driver=$line[23];
829				if(! defined($dumpers_held[$dumpers_active]{$status_driver})) {
830					$dumpers_held[$dumpers_active]{$status_driver}=0;
831				}
832			}
833		}
834		elsif($line[1] eq "FINISHED") {
835			$driver_finished = 1;
836		}
837	}
838	elsif($line[0] eq "dump") {
839		if($line[1] eq "of" &&
840			$line[2] eq "driver" &&
841			$line[3] eq "schedule" &&
842			$line[4] eq "after" &&
843			$line[5] eq "start" &&
844			$line[6] eq "degraded" &&
845			$line[7] eq "mode") {
846			$start_degraded_mode=1;
847		}
848	}
849	elsif($line[0] eq "taper") {
850		if($line[1] eq "wrote") {
851			#1:"wrote" 2:"label" 3:label
852			#$nb_tape++;
853			#$label = $line[3];
854			#$ntlabel{$nb_tape} = $label;
855			#$ntpartition{$nb_tape} = 0;
856			#$ntsize{$nb_tape} = 0;
857			#$ntesize{$nb_tape} = 0;
858		}
859		elsif($line[1] eq "status" && $line[2] eq "file") {
860			#1:"status" #2:"file:" #3:hostname #4:diskname #5:filename
861			#$host = $line[3];
862			#$partition = $line[4];
863			#Which datestamp to use?
864			#$hostpart=&make_hostpart($host,$partition,$datestamp);
865			#assume $hostpart is already set.
866			$taper_status_file{$hostpart} = $line[5];
867		}
868	}
869	elsif($line[0] eq "splitting" &&
870			 $line[1] eq "chunk" &&
871			 $line[2] eq "that" &&
872			 $line[3] eq "started" &&
873			 $line[4] eq "at" &&
874			 $line[6] eq "after") {
875		$line[7] =~ /(\d*)kb/;
876		$size = $1;
877		$ntchunk{$nb_tape}++;
878		$ntsize{$nb_tape} += $size / $unitdivisor;
879		$ntesize{$nb_tape} += $size / $unitdivisor;
880	}
881	else {
882		#print "Ignoring: $lineX\n";
883	}
884}
885
886close(AMDUMP);
887
888if(defined $current_time) {
889	for ($d = 0; $d < $#dumpers_active; $d++) {
890		$the_dumper = "dumper$d";
891		if(defined($running_dumper{$the_dumper}) &&
892		   $running_dumper{$the_dumper} ne "0") {
893			$busy_time{$the_dumper}+=($current_time-$dump_time{$running_dumper{$the_dumper}});
894		}
895	}
896}
897
898print "\n";
899
900$nb_partition = 0;
901
902$epartition = 0;
903$estsize = 0;
904$fpartition = 0;
905$fsize = 0;
906$wpartition = 0;
907$wsize = 0;
908
909$flpartition = 0;
910$flsize = 0;
911$wfpartition = 0;
912$wfsize = 0;
913
914$dtpartition = 0;
915$dtesize = 0;
916$dupartition = 0;
917$dusize = 0;
918$duesize = 0;
919$dpartition = 0;
920$dsize = 0;
921$desize = 0;
922
923$twpartition = 0;
924$twsize = 0;
925$twesize = 0;
926$tapartition = 0;
927$tasize = 0;
928$taesize = 0;
929$tfpartition = 0;
930$tfsize = 0;
931$tfesize = 0;
932$tpartition = 0;
933$tsize = 0;
934$tesize = 0;
935
936$maxnamelength = 10;
937foreach $host (sort @hosts) {
938	foreach $partition (sort @$host) {
939		foreach $datestamp (sort @datestamp) {
940			$hostpart=&make_hostpart($host,$partition,$datestamp);
941			next if(!defined $estimate{$hostpart} && !defined $flush{$hostpart});
942			if(length("$host:$partition") > $maxnamelength) {
943				$maxnamelength = length("$host:$partition");
944			}
945		}
946	}
947}
948
949foreach $host (sort @hosts) {
950	foreach $partition (sort @$host) {
951	   $qpartition = Amanda::Util::quote_string($partition);
952	   foreach $datestamp (sort @datestamp) {
953			$hostpart=&make_hostpart($host,$partition,$datestamp);
954			next if(!defined $estimate{$hostpart} && !defined $flush{$hostpart});
955			$nb_partition++;
956			if( (!defined $size{$hostpart} || $size{$hostpart} == 0) &&
957				 defined $holding_file{$hostpart}) {
958				$size{$hostpart} = &dump_size($holding_file{$hostpart}) / (1024 * $unitdivisor);
959			}
960			$in_flush=0;
961			if($estimate_done != 1 && !defined $flush{$hostpart}) {
962				if(defined $estimate{$hostpart}) {
963					if($estimate{$hostpart} != 1) {
964						if( defined $opt_gestimate ||
965							 defined $opt_failed && $dead_run != 0) {
966							printf "%8s ", $datestamp if defined $opt_date;
967							printf "%-${maxnamelength}s", "$host:$qpartition";
968							print "             ";
969							if ($dead_run) {
970								print " failed: killed while";
971								$exit_status |= $STATUS_FAILED;
972							}
973							print " getting estimate\n";
974						}
975					}
976					else {
977						if(defined $opt_estimate ||
978							(defined $opt_gestimate && $partialestimate{$hostpart} == 1) ||
979							(defined $opt_failed && $dead_run != 0 && $partialestimate{$hostpart} == 1)) {
980							printf "%8s ", $datestamp if defined $opt_date;
981							printf "%-${maxnamelength}s", "$host:$qpartition";
982							printf "%2d ",  $level{$hostpart};
983							printf "%9s$unit", $esize{$hostpart};
984							if($partialestimate{$hostpart} == 1) {
985								if ($dead_run) {
986									print " failed: killed while";
987									$exit_status |= $STATUS_FAILED;
988								}
989								print " partial";
990							}
991							print " estimate done\n";
992						}
993						$epartition++;
994						$estsize += $esize{$hostpart};
995					}
996				}
997			}
998			else {
999				if(defined $estimate{$hostpart}) {
1000					if($estimate{$hostpart} == 1) {
1001						$epartition++;
1002						$estsize += $esize{$hostpart};
1003					}
1004					elsif (!defined $dump_started{$hostpart} || $dump_started{$hostpart} == 0) {
1005						if( defined $opt_failed) {
1006							printf "%8s ", $datestamp if defined $opt_date;
1007							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1008							printf "           no estimate\n";
1009						}
1010						$exit_status |= $STATUS_FAILED;
1011						$fpartition++;
1012						$fsize+=$esize{$hostpart};
1013					}
1014				}
1015				else {
1016					$flpartition++;
1017					$flsize += $size{$hostpart};
1018					$in_flush=1;
1019				}
1020				if(defined $taper_started{$hostpart} &&
1021						$taper_started{$hostpart}==1) {
1022					if(defined $dump_started{$hostpart} &&
1023						$dump_started{$hostpart} == 1 &&
1024							$dump_finished{$hostpart} == -1) {
1025						if(defined $opt_failed) {
1026							printf "%8s ", $datestamp if defined $opt_date;
1027							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1028							printf "%9s$unit", $esize{$hostpart};
1029							print " dump to tape failed: " . $error{$hostpart};
1030							print "\n";
1031						}
1032						$exit_status |= $STATUS_FAILED;
1033						$fpartition++;
1034						$fsize+=$esize{$hostpart};
1035					} elsif(defined $dump_started{$hostpart} &&
1036						$dump_started{$hostpart} == 1 &&
1037							$dump_finished{$hostpart} == 0 &&
1038							$taper_started{$hostpart} == 1) {
1039						if( defined $opt_dumpingtape ||
1040							 defined $opt_failed && $dead_run != 0) {
1041							printf "%8s ", $datestamp if defined $opt_date;
1042							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1043							printf "%9s$unit", $esize{$hostpart};
1044							if ($dead_run) {
1045								print " failed: killed while";
1046								$exit_status |= $STATUS_FAILED;
1047							}
1048							print " dumping to tape";
1049							$size = $tapedsize{$hostpart};
1050							if ($taper_status_file{$hostpart} && -f $taper_status_file{$hostpart} &&
1051								open FF, "<$taper_status_file{$hostpart}") {
1052								$line = <FF>;
1053								if (defined $line) {
1054									chomp $line;
1055									$value = $line / ($unitdivisor * 1024);
1056									if ($value) {
1057										$size = $value if (!defined($size) || $value > $size);
1058									}
1059								}
1060								close FF;
1061							}
1062							if(defined($size)) {
1063								printf " (%s$unit done (%0.2f%%))", $size, 100.0 * $size/$esize{$hostpart};
1064								$dtsize += $size;
1065							}
1066							if( defined $starttime ) {
1067								print " (", &showtime($taper_time{$hostpart}), ")";
1068							}
1069							print "\n";
1070						}
1071						$dtpartition++;
1072						$dtesize += $esize{$hostpart};
1073					}
1074					elsif($taper_finished{$hostpart} == 0) {
1075						if( defined $opt_writingtape ||
1076							 defined $opt_failed && $dead_run != 0) {
1077							printf "%8s ", $datestamp if defined $opt_date;
1078							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1079							printf "%9s$unit", $size{$hostpart};
1080							if ($dead_run) {
1081								print " failed: killed while";
1082								$exit_status |= $STATUS_FAILED;
1083							}
1084							if($in_flush == 0) {
1085								if (defined $dump_finished{$hostpart}) {
1086									if ($dump_finished{$hostpart} == 1) {
1087										print " dump done,";
1088									} else {
1089										$exit_status |= $STATUS_FAILED;
1090										print " dump failed: ", $error{$hostpart}, ",";
1091										$fpartition++;
1092										$fsize+=$esize{$hostpart};
1093									}
1094								}
1095								print " writing to tape";
1096							}
1097							else {
1098								print " flushing to tape";
1099							}
1100							$size = $tapedsize{$hostpart};
1101							if ($taper_status_file{$hostpart} &&  -f $taper_status_file{$hostpart} &&
1102								open FF, "<$taper_status_file{$hostpart}") {
1103								$line = <FF>;
1104								if (defined $line) {
1105									chomp $line;
1106									$value = $line / ($unitdivisor * 1024);
1107									if ($value) {
1108										$size = $value if (!defined($size) || $value > $size);
1109									}
1110								}
1111								close FF;
1112							}
1113							if(defined($size) and defined($size{$hostpart}) and $size{$hostpart} > 0) {
1114								printf " (%s$unit done (%0.2f%%))", $size, 100.0 * $size/$size{$hostpart};
1115							}
1116							if( defined $starttime ) {
1117								print " (", &showtime($taper_time{$hostpart}), ")";
1118							}
1119							print ", ", $error{$hostpart} if (defined($error{$hostpart}) &&
1120															  $error{$hostpart} ne "" &&
1121															  (!defined $dump_finished{$hostpart} ||
1122															   $dump_finished{$hostpart} != -3));
1123							print "\n";
1124						}
1125						$tapartition++;
1126						$tasize += $size{$hostpart};
1127						if(defined $esize{$hostpart}) {
1128							$taesize += $esize{$hostpart};
1129						}
1130						else {
1131							$taesize += $size{$hostpart};
1132						}
1133						if (defined $dump_finished{$hostpart} && $dump_finished{$hostpart} == 1) {
1134							$dpartition++;
1135							$dsize += $size{$hostpart};
1136							if(defined $esize{$hostpart} && $esize{$hostpart} > 1) {
1137								$desize += $esize{$hostpart};
1138							} else {
1139								$desize += $size{$hostpart};
1140							}
1141						}
1142					}
1143					elsif($taper_finished{$hostpart} < 0) {
1144
1145						if(defined $size{$hostpart}) {
1146							$xsize = $size{$hostpart};
1147						}
1148						elsif(defined $esize{$hostpart}) {
1149							$xsize = $esize{$hostpart};
1150						}
1151						else {
1152							$xsize = 0;
1153						}
1154
1155						if(defined $esize{$hostpart}) {
1156							$exsize += $esize{$hostpart};
1157						}
1158						else {
1159							$exsize += $xsize;
1160						}
1161
1162						if( defined $opt_failed  ||
1163							 (defined $opt_waittaper && ($taper_finished{$hostpart} == -1))) {
1164							printf "%8s ", $datestamp if defined $opt_date;
1165							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1166							printf "%9s$unit", $xsize;
1167							print " dump done," if defined $dump_finished{$hostpart} && $dump_finished{$hostpart} == 1;
1168							if($in_flush == 0) {
1169								if ($tape_config{$hostpart}) {
1170									print " taping delayed because of config";
1171								} else {
1172									print " failed to tape";
1173								}
1174							}
1175							else {
1176								if ($tape_config{$hostpart}) {
1177									print " flushing delayed because of config";
1178								} else {
1179									print " failed to flush";
1180								}
1181							}
1182							print ": ",$error{$hostpart} if defined $error{$hostpart};
1183
1184							print " (will retry)" unless $taper_finished{$hostpart} < -1;
1185							if( defined $starttime ) {
1186								print " (", &showtime($taper_time{$hostpart}), ")";
1187							}
1188							print "\n";
1189						}
1190						$exit_status |= $STATUS_TAPE;
1191
1192						$tfpartition++;
1193						$tfsize += $xsize;
1194						$tfesize += $exsize;
1195
1196						if($in_flush == 0) {
1197							$twpartition++;
1198							$twsize += $xsize;
1199							$twesize += $exsize;
1200						}
1201						else {
1202							$wfpartition++;
1203							$wfsize += $xsize;
1204						}
1205						if (defined $dump_finished{$hostpart} && $dump_finished{$hostpart} == 1) {
1206							$dpartition++;
1207							$dsize += $size{$hostpart};
1208							if(defined $esize{$hostpart} && $esize{$hostpart} > 1) {
1209								$desize += $esize{$hostpart};
1210							} else {
1211								$desize += $size{$hostpart};
1212							}
1213						}
1214					}
1215					elsif($taper_finished{$hostpart} == 1) {
1216						if( defined $opt_finished ) {
1217							printf "%8s ", $datestamp if defined $opt_date;
1218							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1219							printf "%9s$unit", $size{$hostpart};
1220							if($in_flush == 0) {
1221								if (defined $dump_finished{$hostpart} && $dump_finished{$hostpart} == -3) {
1222									$exit_status |= $STATUS_FAILED;
1223									print " dump failed: ", $error{$hostpart}, ",";
1224									$fpartition++;
1225									$fsize+=$esize{$hostpart};
1226								}
1227								print " finished";
1228							}
1229							else {
1230								print " flushed";
1231							}
1232							if( defined $starttime ) {
1233								print " (", &showtime($taper_time{$hostpart}), ")";
1234							}
1235							if(defined $partial{$hostpart} && $partial{$hostpart} == 1) {
1236								print ", PARTIAL";
1237								$exit_status |= $STATUS_FAILED;
1238							}
1239							print "\n";
1240						}
1241						if (defined $dump_finished{$hostpart} && $dump_finished{$hostpart} == 1) {
1242							$dpartition++;
1243							$dsize += $size{$hostpart};
1244							if(defined $esize{$hostpart} && $esize{$hostpart} > 1) {
1245								$desize += $esize{$hostpart};
1246							} else {
1247								$desize += $size{$hostpart};
1248							}
1249						}
1250						$tpartition++;
1251						$tsize += $size{$hostpart};
1252						if(defined $esize{$hostpart} && $esize{$hostpart} > 1) {
1253							$tesize += $esize{$hostpart};
1254						}
1255						else {
1256							$tesize += $size{$hostpart};
1257						}
1258					}
1259					else {
1260						printf "%8s ", $datestamp if defined $opt_date;
1261						printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1262						print " unknown state TAPER\n";
1263					}
1264				}
1265				elsif(defined $dump_started{$hostpart}) {
1266					if($dump_started{$hostpart} == -1) {
1267						if( defined $opt_failed ) {
1268							printf "%8s ", $datestamp if defined $opt_date;
1269							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1270							printf "failed: " . $error{$hostpart} . "\n";
1271						}
1272						$exit_status |= $STATUS_FAILED;
1273
1274						$fpartition++;
1275						$fsize+=$esize{$hostpart};
1276					}
1277					elsif($dump_started{$hostpart} == 0) {
1278						if($estimate{$hostpart} == 1) {
1279							if( defined $opt_waitdumping ) {
1280								printf "%8s ", $datestamp if defined $opt_date;
1281								printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1282								printf "%9s$unit", $esize{$hostpart};
1283								if ($dead_run) {
1284									print " failed: process terminated while";
1285									$exit_status |= $STATUS_FAILED;
1286								}
1287								print " waiting for dumping $error{$hostpart}\n";
1288							}
1289							if($driver_finished == 1) {
1290								$exit_status |= $STATUS_MISSING;
1291							}
1292							$wpartition++;
1293							$wsize += $esize{$hostpart};
1294						}
1295					}
1296					elsif($dump_started{$hostpart} == 1 &&
1297							($dump_finished{$hostpart} == -1 ||
1298						    $dump_finished{$hostpart} == -3)) {
1299						if( defined $opt_failed ) {
1300							printf "%8s ", $datestamp if defined $opt_date;
1301							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1302							print "backup failed: ", $error{$hostpart};
1303							if( defined $starttime ) {
1304								print " (", &showtime($dump_time{$hostpart}), ")";
1305							}
1306							print "\n";
1307						}
1308						$exit_status |= $STATUS_FAILED;
1309						$fpartition++;
1310						$fsize+=$esize{$hostpart};
1311					}
1312					elsif($dump_started{$hostpart} == 1 &&
1313							$dump_finished{$hostpart} == 0) {
1314						if( defined $opt_dumping ||
1315							 defined $opt_failed && $dead_run != 0) {
1316							printf "%8s ", $datestamp if defined $opt_date;
1317							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1318							printf "%9s$unit", $esize{$hostpart};
1319							if ($dead_run) {
1320								print " failed: killed while";
1321								$exit_status |= $STATUS_FAILED;
1322							}
1323							printf " dumping %8s$unit", $size{$hostpart};
1324							if($size{$hostpart} != 0) {
1325								printf " (%6.2f%%)", (100.0*$size{$hostpart})/$esize{$hostpart};
1326							}
1327							if( defined $starttime ) {
1328								print " (", &showtime($dump_time{$hostpart}), ")";
1329							}
1330							if(defined $dump_roomq{$hostpart}) {
1331								print " " . $error{$hostpart};
1332							}
1333							print "\n";
1334						}
1335						$dupartition++;
1336						$dusize += $size{$hostpart};
1337						$duesize += $esize{$hostpart};
1338					}
1339					elsif($dump_finished{$hostpart} == 1 &&
1340							$taper_started{$hostpart} != 1) {
1341						if( defined $opt_waittaper ) {
1342							printf "%8s ", $datestamp if defined $opt_date;
1343							printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1344							printf "%9s$unit", $size{$hostpart};
1345							print " dump done";
1346							if( defined $starttime ) {
1347								print " (", &showtime($dump_time{$hostpart}), ")";
1348							}
1349							print ",";
1350							if ($dead_run) {
1351								print " process terminated while";
1352							}
1353							print " waiting for writing to tape";
1354							if(defined $partial{$hostpart} && $partial{$hostpart} == 1) {
1355								print ", PARTIAL";
1356								$exit_status |= $STATUS_FAILED;
1357							}
1358							print "\n";
1359						}
1360						$dpartition++;
1361						$dsize += $size{$hostpart};
1362						$desize += $esize{$hostpart};
1363						$twpartition++;
1364						$twsize += $size{$hostpart};
1365						$twesize += $esize{$hostpart};
1366					}
1367					else {
1368						printf "%8s ", $datestamp if defined $opt_date;
1369						printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1370						print " unknown state DUMPER\n";
1371					}
1372				}
1373				elsif(defined $flush{$hostpart}) {
1374					if( defined $opt_waittaper ) {
1375						printf "%8s ", $datestamp if defined $opt_date;
1376						printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1377						printf "%9s$unit", $size{$hostpart};
1378						if ($dead_run) {
1379							print " process terminated while";
1380						}
1381						print " waiting to flush";
1382						if(defined $partial{$hostpart} && $partial{$hostpart} == 1) {
1383							print ", PARTIAL";
1384							$exit_status |= $STATUS_FAILED;
1385						}
1386						print "\n";
1387					}
1388					$wfpartition++;
1389					$wfsize += $size{$hostpart};
1390				}
1391				elsif(defined $level{$hostpart}) {
1392					printf "%8s ", $datestamp if defined $opt_date;
1393					printf "%-${maxnamelength}s%2d ", "$host:$qpartition", $level{$hostpart};
1394					print " unknown state\n";
1395				}
1396			}
1397		}
1398	}
1399}
1400
1401if (defined $opt_summary) {
1402	print "\n";
1403	print  "SUMMARY          part      real  estimated\n";
1404	print  "                           size       size\n";
1405	printf "partition       : %3d\n", $nb_partition;
1406	printf "estimated       : %3d %20s$unit\n", $epartition , int($estsize);
1407	printf "flush           : %3d %9s$unit\n", $flpartition, int($flsize);
1408	printf "failed          : %3d %20s$unit           (%6.2f%%)\n",
1409		$fpartition , int($fsize),
1410		$estsize ? ($fsize * 1.0 / $estsize) * 100 : 0.0;
1411	printf "wait for dumping: %3d %20s$unit           (%6.2f%%)\n",
1412		$wpartition , int($wsize),
1413		$estsize ? ($wsize * 1.0 / $estsize) * 100 : 0.0;
1414	if(defined($dtsize)) {
1415		printf "dumping to tape : %3d %9s$unit %9s$unit (%6.2f%%) (%6.2f%%)\n",
1416			$dtpartition, int($dtsize), int($dtesize),
1417			$dtsize ? ($dtsize * 1.0 / $dtesize) * 100 : 0.0,
1418			$estsize ? ($dtesize * 1.0 / $estsize) * 100 : 0.0;
1419	} else {
1420		printf "dumping to tape : %3d %20dsunit           (%6.2f%%)\n",
1421			$dtpartition, int($dtesize),
1422			$estsize ? ($dtesize * 1.0 / $estsize) * 100 : 0.0;
1423	}
1424	printf "dumping         : %3d %9s$unit %9s$unit (%6.2f%%) (%6.2f%%)\n",
1425		$dupartition, int($dusize), int($duesize),
1426		$duesize ? ($dusize * 1.0 / $duesize) * 100 : 0.0,
1427		$estsize ? ($dusize * 1.0 / $estsize) * 100 : 0.0;
1428	printf "dumped          : %3d %9s$unit %9s$unit (%6.2f%%) (%6.2f%%)\n",
1429		$dpartition , int($dsize) , int($desize),
1430		$desize ? ($dsize * 1.0 / $desize) * 100 : 0.0,
1431		$estsize ? ($dsize * 1.0 / $estsize) * 100 : 0.0;
1432	printf "wait for writing: %3d %9s$unit %9s$unit (%6.2f%%) (%6.2f%%)\n",
1433		$twpartition, int($twsize), int($twesize),
1434		$twesize ? ($twsize * 1.0 / $twesize) * 100 : 0.0,
1435		$estsize ? ($twsize * 1.0 / $estsize) * 100 : 0.0;
1436	printf "wait to flush   : %3d %9s$unit %9s$unit (%6.2f%%) (%6.2f%%)\n",
1437		$wfpartition, int($wfsize), int($wfsize), 100, 0;
1438	printf "writing to tape : %3d %9s$unit %9s$unit (%6.2f%%) (%6.2f%%)\n",
1439		$tapartition, int($tasize), int($taesize),
1440		$taesize ? ($tasize * 1.0 / $taesize) * 100 : 0.0,
1441		$estsize ? ($tasize * 1.0 / $estsize) * 100 : 0.0;
1442	printf "failed to tape  : %3d %9s$unit %9s$unit (%6.2f%%) (%6.2f%%)\n",
1443		$tfpartition, int($tfsize), int($tfesize),
1444		$tfesize ? ($tfsize * 1.0 / $tfesize) * 100 : 0.0,
1445		$estsize ? ($tfsize * 1.0 / $estsize) * 100 : 0.0;
1446	printf "taped           : %3d %9s$unit %9s$unit (%6.2f%%) (%6.2f%%)\n",
1447		$tpartition , int($tsize) , int($tesize),
1448		$tesize ? ($tsize * 1.0 / $tesize) * 100 : 0.0,
1449		($estsize+$flsize) ? ($tsize * 1.0 / ($estsize + $flsize)) * 100 : 0.0;
1450	if($nb_tape > 1 || $tape_size != 0) {
1451		for($i=1; $i <= $nb_tape; $i++) {
1452			if($tape_size != 0) {
1453				printf "  tape %-3d      : %3d %9s$unit %9s$unit (%6.2f%%) %s",
1454					$i, $ntpartition{$i}, int($ntsize{$i}), int($ntesize{$i}), 100*$ntsize{$i}/$tape_size, $ntlabel{$i};
1455			}
1456			else {
1457				printf "  tape %-3d      : %3d %9s$unit %9s$unit %s",
1458					$i, $ntpartition{$i}, int($ntsize{$i}), int($ntesize{$i}), $ntlabel{$i};
1459			}
1460			if(defined($ntchunk{$i}) && $ntchunk{$i} > 0) {
1461				printf " (%d chunks)", $ntchunk{$i};
1462			}
1463			print "\n";
1464		}
1465	}
1466	if($idle_dumpers == 0) {
1467		printf "all dumpers active\n";
1468	}
1469	else {
1470		$c1 = ($idle_dumpers == 1) ? "" : "s";
1471		$c2 = ($idle_dumpers < 10) ? " " : "";
1472		$c3 = ($idle_dumpers == 1) ? " " : "";
1473		printf "%d dumper%s idle%s %s: %s\n", $idle_dumpers, $c1, $c2, $c3, $status_driver;
1474	}
1475
1476	foreach $worker (sort keys %status_taper) {
1477		$num = $worker;
1478	   $num =~ s/worker//g;
1479		print "taper $num status: $status_taper{$worker}\n";
1480   }
1481	#printf "taper status: $status_taper\n";
1482	if (defined $qlen{"tapeq"}) {
1483		printf "taper qlen: %d\n", $qlen{"tapeq"};
1484	}
1485	if (defined ($free{"kps"})) {
1486		printf "network free kps: %9d\n", $free{"kps"};
1487	}
1488	if (defined ($free{"space"})) {
1489		if ($holding_space) {
1490			$hs = ($free{"space"} * 1.0 / $holding_space) * 100;
1491		} else {
1492			$hs = 0.0;
1493		}
1494		printf "holding space   : %9s$unit (%6.2f%%)\n", int($free{"space"}/$unitdivisor), $hs;
1495	}
1496}
1497
1498if(defined $opt_stats) {
1499	if(defined($current_time) and defined($start_time) and
1500	   $current_time != $start_time) {
1501		$total_time=$current_time-$start_time;
1502		foreach $key (sort byprocess keys %busy_time) {
1503			printf "%8s busy   : %8s  (%6.2f%%)\n",
1504				$key, &busytime($busy_time{$key}),
1505				($busy_time{$key} * 1.0 / $total_time) * 100;
1506		}
1507		for ($d = 0; $d <= $#dumpers_active; $d++) {
1508			$l = sprintf "%2d dumper%s busy%s : %8s  (%6.2f%%)",
1509				$d, ($d == 1) ? "" : "s", ($d == 1) ? " " : "",
1510				&busytime($dumpers_active[$d]),
1511				($dumpers_active[$d] * 1.0 / $total_time) * 100;
1512			print $l;
1513			$s1 = "";
1514			$s2 = " " x length($l);
1515			$r = $dumpers_held[$d];
1516			foreach $key (sort valuesort keys %$r) {
1517				next
1518				  unless $dumpers_held[$d]{$key} >= 1;
1519				printf "%s%20s: %8s  (%6.2f%%)\n",
1520					$s1,
1521					$key,
1522					&busytime($dumpers_held[$d]{$key}),
1523					($dumpers_held[$d]{$key} * 1.0 / $dumpers_active[$d]) * 100;
1524				$s1 = $s2;
1525			}
1526			if ($s1 eq "") {
1527				print "\n";
1528			}
1529		}
1530	}
1531}
1532
1533exit $exit_status;
1534
1535sub make_hostpart() {
1536	local($host,$partition,$datestamp) = @_;
1537
1538	if(! defined($hosts{$host})) {
1539		push @hosts, $host;
1540		$hosts{$host}=1;
1541	}
1542	my($new_part) = 1;
1543	foreach $pp (sort @$host) {
1544		$new_part = 0 if ($pp eq $partition);
1545	}
1546	push @$host, $partition if $new_part==1;
1547
1548	my($hostpart) = "$host$partition$datestamp";
1549	if(!defined $datestamp{$datestamp}) {
1550		$datestamp{$datestamp} = 1;
1551		push @datestamp, $datestamp;
1552	}
1553
1554	return $hostpart;
1555}
1556
1557sub byprocess() {
1558	my(@tmp_a) = split(/(\d*)$/, $a, 2);
1559	my(@tmp_b) = split(/(\d*)$/, $b, 2);
1560	return ($tmp_a[0] cmp $tmp_b[0]) || ($tmp_a[1] <=> $tmp_b[1]);
1561}
1562
1563sub valuesort() {
1564	$r->{$b} <=> $r->{$a};
1565}
1566
1567sub dump_size() {
1568	local($filename) = @_;
1569	local($size);
1570	local($dsize) = 0;
1571	local($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
1572		   $atime,$mtime,$ctime,$blksize,$blocks);
1573	while ($filename ne "") {
1574		$filename = "$filename.tmp" if (!(-e "$filename"));
1575		$filename = "/dev/null" if (!(-e "$filename"));
1576		($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
1577				$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
1578		$size=$size-32768 if $size > 32768;
1579		$dsize += $size;
1580		open(DUMP,$filename);
1581		$filename = "";
1582		while(<DUMP>) {
1583			if(/^CONT_FILENAME=(.*)$/) { $filename = $1; last }
1584			last if /^To restore, position tape at start of file and run/;
1585		}
1586		close(DUMP);
1587	}
1588	return $dsize;
1589}
1590
1591sub unctime() {
1592	my (@MoY);
1593	my (@tl);
1594	my ($a);
1595	my ($m);
1596	my ($month);
1597	my ($time);
1598
1599	@MoY = ('Jan','Feb','Mar','Apr','May','Jun',
1600		'Jul','Aug','Sep','Oct','Nov','Dec');
1601
1602	# Preset an array of values in case some parts are not passed as
1603	# arguments.  This lets the date, etc, be omitted and default to
1604	# today.
1605
1606	@tl = localtime;
1607
1608	foreach $a (@_) {
1609		next
1610		  if ($a eq '');
1611
1612		# See if this argument looks like a month name.
1613
1614		$month = 0;
1615		foreach $m (@MoY) {
1616			last
1617			  if ($m eq $a);
1618			$month = $month + 1;
1619		}
1620		if ($month < 12) {
1621			$tl[4] = $month;
1622			next;
1623		}
1624
1625		# See if this is a day of the month.
1626
1627		if ($a =~ /^\d+$/ && $a >= 1 && $a <= 32) {
1628			$tl[3] = $a;
1629			next;
1630		}
1631
1632		# See if the next argument looks like a time.
1633
1634		if ($a =~ /^(\d+):(\d+)/) {
1635			$tl[2] = $1;
1636			$tl[1] = $2;
1637			if ($a =~ /^(\d+):(\d+):(\d+)/) {
1638				$tl[0] = $3;
1639			}
1640			next;
1641		}
1642
1643		# See if this is a year.
1644
1645		if ($a =~ /^\d\d\d\d$/ && $a >= 1900) {
1646			$tl[5] = $a;
1647			next;
1648		}
1649	}
1650
1651	$time = &timelocal (@tl);
1652
1653	return $time;
1654}
1655
1656sub set_starttime() {
1657	my (@tl);
1658	my ($time);
1659	my ($date);
1660
1661	# Preset an array of values in case some parts are not passed as
1662	# arguments.  This lets the date, etc, be omitted and default to
1663	# today.
1664
1665	($date)=@_;
1666	@tl = localtime;
1667
1668	$tl[5] = substr($date,  0, 4)   if(length($date) >= 4);
1669	$tl[4] = substr($date,  4, 2)-1 if(length($date) >= 6);
1670	$tl[3] = substr($date,  6, 2)   if(length($date) >= 8);
1671	$tl[2] = substr($date,  8, 2)   if(length($date) >= 10);
1672	$tl[1] = substr($date, 10, 2)   if(length($date) >= 12);
1673	$tl[0] = substr($date, 12, 2)   if(length($date) >= 14);
1674
1675	$time = &timelocal (@tl);
1676
1677	return $time;
1678}
1679
1680
1681sub showtime() {
1682	my($delta) = shift;
1683	my($oneday) = 24*60*60;
1684
1685	my @starttime = localtime($starttime);
1686	my @now = localtime($starttime+$delta);
1687	$now_yday = $now[7];
1688
1689	# leap year
1690	if ($starttime[5] < $now[5]) {
1691		my $days_in_year = 364;
1692		my $startime1 = $starttime;
1693		while ($startime1 < $starttime+$delta) {
1694			my @starttime1 = localtime($starttime);
1695			if ($starttime1[7] > $days_in_year) {
1696				$days_in_year = $starttime1[7];
1697			}
1698			$startime1 += $oneday;
1699		}
1700		$now_yday += $days_in_year+1;
1701	}
1702
1703	if ($starttime[7] < $now_yday) {
1704		$result=sprintf("%d+", $now_yday - $starttime[7]);
1705	} else {
1706		$result="";
1707	}
1708	$result.=sprintf("%d:%02d:%02d",$now[2],$now[1],$now[0]);
1709	return $result;
1710}
1711
1712sub busytime() {
1713	my($busy)=shift;
1714	my($oneday)=24*60*60;
1715
1716	if($busy > $oneday) {
1717		$days=int($busy/$oneday);
1718		$result=sprintf("%d+",$busy/$oneday);
1719		$busy-=$days*$oneday;
1720	} else {
1721		$result="";
1722	}
1723	$hours=int($busy/60/60);
1724	$busy-=$hours*60*60;
1725	$minutes=int($busy/60);
1726	$busy-=$minutes*60;
1727	$seconds=$busy;
1728	$result.=sprintf("%d:%02d:%02d",$hours,$minutes,$seconds);
1729	return $result;
1730}
1731
1732