#!/usr/bin/perl -w # Backups Blow by Brandon Low# # Copyright 2007 Brandon Low # Released under the terms of the GPL v2 # # http://lostlogicx.com/backupsblow/ package BackupsBlow::Util; use strict; use Exporter; our (@EXPORT_OK, %EXPORT_TAGS, @ISA); @ISA = qw(Exporter); @EXPORT_OK = qw(&buildTarCommand &printTime &performMounts &performUnmounts); %EXPORT_TAGS = ("all" => [@EXPORT_OK], "transport" => [qw(&buildTarCommand)]); use BackupsBlow::Config qw(:read); use BackupsBlow::Programs qw(:all); use Log::Log4perl; my $logger=Log::Log4perl->get_logger(); # Globals, needed to improve the chances of an error leaving # the system in a predictable state. my @ejects; my @unmounts; sub performMounts() { if (my @inserts=configArray("EJECTS")) { my $eject=needProgram("eject"); foreach my $insert(@inserts) { system(my $command="$eject -t $insert"); die("Command failed: $command: $?") unless ($? == 0); push @ejects,$insert; } } if (my @mounts=configArray("MOUNTS")) { my $mount=needProgram("mount"); foreach my $mountPoint(@mounts) { system(my $command="$mount $mountPoint"); die("Command failed: $command: $?") unless ($? == 0); push @unmounts,$mountPoint; } } } sub performUnmounts() { if (@unmounts) { if (my $umount=program("umount")) { while (my $mountPoint=pop @unmounts) { system(my $command="$umount $mountPoint"); unless ($? == 0) { # Consider these non-fatal, but warn the sysadmin $logger->logwarn("Command failed: $command: $?"); } } } else { $logger->logwarn("umount not found in path"); } } if (@ejects) { if (my $eject=program("eject")) { while (my $ejectDev=pop @ejects) { system(my $command="$eject $ejectDev"); unless ($? == 0) { # Consider these non-fatal, but warn the sysadmin $logger->logwarn("Command failed: $command: $?"); } } } else { $logger->logwarn("eject not found in path"); } } } sub printTime ($$) { my ($name,$time)=@_; my $hours=int($time/3600); my $minutes=int(($time-3600*$hours)/60); my $seconds=$time-3600*$hours-60*$minutes; print "\u$name elapsed time: "; my $result=""; $result.=sprintf "%dh",$hours if ($hours != 0); $result.=sprintf "%02dm",$minutes if ($hours != 0 || $minutes != 0); $result.=sprintf "%02ds\n",$seconds; $result=~s/^0([0-9])/$1/; print $result; } # Build an incremental tar command sub buildTarCommand ($$) { my ($incFile,$errFile)=@_; my @dirs=needConfigArray("DIRS"); foreach my $dir(@dirs) { die("A specified directory is not a directory") unless ( -d $dir ); } my $tar=needProgram("tar"); my $compression=needProgram(config("COMPRESSION_PROGRAM","gzip")); my @tarCommand; push @tarCommand,$tar; push @tarCommand,"-g $incFile"; push @tarCommand,"--use-compress-program $compression"; push @tarCommand,"-c"; my @excludes=configArray("EXCLUDES"); foreach my $exclude(@excludes) { push @tarCommand,"--exclude=\"$exclude\""; } push @tarCommand,@dirs; push @tarCommand,"2>"; push @tarCommand,$errFile; return join(" ", @tarCommand); } sub handler { my $sig = $_[0]; die("Caught signal: $sig"); } sub show_call_stack($) { my $logFunc = $_[0]; my ($path, $line, $subr); my $max_depth = 30; my $i = 1; &$logFunc("--- Begin stack trace ---"); while ( (my @call_details = (caller($i++))) && ($i<$max_depth) ) { &$logFunc($call_details[1]. " line ".$call_details[2]. " in function ".$call_details[3]); } &$logFunc("--- End stack trace ---"); } sub cleanDeath { # Don't change die behavior in the parser if (defined $^S) { performUnmounts(); $logger->error(@_); show_call_stack(sub {$logger->debug(@_)}); exit(1); } } $SIG{__DIE__} = \&cleanDeath; $SIG{'INT'} = \&handler; $SIG{'TERM'} = \&handler; $SIG{'QUIT'} = \&handler; 1;
"People should not be afraid of their governments. Governments should be afraid of their people." --V