Saturday, March 31, 2007

Automatic backup script in Perl

An automatic backup script is a very handy tool so I can "forget" the backup task everyday. It's also an exercise after I read Perl tutorial. Now, it's scheduled and run everyday via crontab on my Linux server at home.

Basically it's a lightweight backup tool. First it archive files to backup. Then it compares to last backup archive. If it's different, the archive is ftped to a remote server. A brief log and a detailed log are generated meanwhile.

It works pretty well and stable. The source code is shown as following.

#!/usr/bin/perl

use strict;
use warnings;

my ($logdir, $log, $detailedlogdir, $detailedlog, $lastsuccessfilename);
my ($targetdir, $target, $targetprefix, $targetnameonly);
my $cbserver;
my @source;
my ($timeforfilename, $timestring);

sub get_timestring {
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdat);

($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdat) = localtime;

$year+=1900;
$mon = sprintf("%02d", $mon+1);
$mday = sprintf("%02d", $mday);

$timeforfilename = $year.$mon.$mday;
return $year."/".$mon."/".$mday."-".$hour.":".$min.":".$sec;

}

sub print_usage {

print "usage: squarrel daily|weekly|monthly\n";

}

sub get_argv {

if ($#ARGV!=0) {
&print_usage;
exit;
}

if ($ARGV[0] eq "daily") {
@source="..."; # file or directories to backup on daily base.
} elsif ($ARGV[0] eq "weekly") {
@source="..."; # file or directories to backup on weekly base.
} elsif ($ARGV[0] eq "monthly") {
@source="..."; # file or directories to backup on monthly base.
} else {
&print_usage;
exit;
}

$targetprefix=$ARGV[0];

}

sub init {

$logdir = "/var/www/html"; # log is written in httpd directory so I can check it via browser
$log = "$logdir"."/emc.log";
$detailedlogdir = "/home/emc";
$detailedlog = "$detailedlogdir"."/emcdetailed.log";

if (-e $log) {
open LOGFILE, ">> $log" or die $!;
} else {
open LOGFILE, "> $log" or die $!;
}

if (-e $detailedlog) {
open DETAILEDLOG, ">> $detailedlog" or die $!;
} else {
open DETAILEDLOG, "> $detailedlog" or die $!;
}

print LOGFILE "----------------------------------------------------\n";
print DETAILEDLOG "----------------------------------------------------\n";

$timestring = &get_timestring;
print LOGFILE "$timestring backup($targetprefix) starts\n";
print DETAILEDLOG "$timestring backup($targetprefix) starts\n";

$targetdir = "/home/bt/crossbackup";
# $targetnameonly = "backup".$timeforfilename.".tar.bz2";
$targetnameonly=$targetprefix.$timeforfilename.".tar.bz2";
$target = "$targetdir/".$targetnameonly;

$lastsuccessfilename = "$detailedlogdir"."/lastsuccess_".$targetprefix;
$cbserver = "..."; # remote server address

}

sub archive {
my ($archivecmd, $result);

$archivecmd = "tar cvfj $target @source";

$timestring = &get_timestring;
print LOGFILE "$timestring $archivecmd\n";
print DETAILEDLOG "$timestring $archivecmd\n";

system("$archivecmd >>$detailedlog 2>> $detailedlog");
$result = $?;
$timestring = &get_timestring;

if ($result == 0) {
print LOGFILE "$timestring archiving finish successfully\n";
print DETAILEDLOG "$timestring archiving finish successfully\n";
} else {
print LOGFILE "$timestring archiving fail $result\n";
print DETAILEDLOG "$timestring archiving fail $result\n";
}

return $result;
}

sub check_identical {
my $lastsuccesstarget;
my $different;

if (-e $lastsuccessfilename) {
open LASTSUCCESS, "$lastsuccessfilename" or die "Write $lastsuccessfilename";
$lastsuccesstarget=;
chop($lastsuccesstarget);
close LASTSUCCESS;

$timestring = &get_timestring;
print LOGFILE "$timestring compare $target $lastsuccesstarget\n";
print DETAILEDLOG "$timestring compare $target $lastsuccesstarget\n";

system("diff -q $lastsuccesstarget $target >> $detailedlog 2>> $detailedlog");
if ($?) {
$timestring = &get_timestring;
print LOGFILE "$timestring $target is different to $lastsuccesstarget\n";
print DETAILEDLOG "$timestring $target is different $lastsuccesstarget\n";
return 1;
} else {
$timestring = &get_timestring;
print LOGFILE "$timestring $target is identical to $lastsuccesstarget\n";
print DETAILEDLOG "$timestring $target is identical to $lastsuccesstarget\n";
system("rm -f $lastsuccesstarget\n");
$timestring = &get_timestring;
print LOGFILE "$timestring remove $lastsuccesstarget\n";
print DETAILEDLOG "$timestring remove $lastsuccesstarget\n";
return 0;
}
} else {
return 1;
}

}

sub ftp_upload {
my $ftpcmd;
my $result;

$ftpcmd = '"user username password\nbinary\nput '.$target.' '.$targetnameonly.'\nquit\n"';

$timestring = &get_timestring;
print LOGFILE "$timestring ftp -v -d $cbserver <<$ftpcmd\n"; print DETAILEDLOG "$timestring ftp -v -d $cbserver <<$ftpcmd\n"; system("echo -e $ftpcmd | ftp -v -d $cbserver>> $log 2>> $log");
$result=$?;

$timestring = &get_timestring;
if ($result == 0) {
print LOGFILE "$timestring uploading finish successfully\n";
print DETAILEDLOG "$timestring uploading finish successfully\n";
} else {
print LOGFILE "$timestring uploading fail $result\n";
print DETAILEDLOG "$timestring uploading fail $result\n";
}
return $result;
}

sub done {
my ($result, $stage) = @_;

$timestring = &get_timestring;
if ($result == 0) {
open LASTSUCCESS, ">$lastsuccessfilename" or die "Write $lastsuccessfilename";
print LOGFILE "$timestring backup($targetnameonly) succeed\n";
print DETAILEDLOG "$timestring backup($targetnameonly) succeed\n";
print LASTSUCCESS "$target\n";
close LASTSUCCESS;
} else {
print LOGFILE "$timestring backup($targetnameonly) failed in stage $stage\n";
print DETAILEDLOG "$timestring backup($targetnameonly) failed in stage $stage\n";
}
close LOGFILE;
close DETAILEDLOG;

}

my $result;

&get_argv;
&init;

$result = &archive;
if ($result) {
&done(1, "archive");
exit (1);
}

$result = &check_identical;
if ($result==0) {
&done(0, "");
exit(0);
}

$result = &ftp_upload;
if ($result) {
&done(2, "upload");
exit (2);
}

&done(0, "");
exit(0);