| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- #!/usr/bin/perl
- use Cwd;
- use File::Basename;
- use File::Find;
- use File::stat qw(:FIELDS);
- use File::Spec::Functions;
- use Sys::Hostname;
- use DirHandle;
- use Time::localtime;
- use Fcntl;
- use Tie::File;
- use Net::FTP;
- use Data::Dumper;
- my $WAIT_TIME = 10800; # 3 hours
- my @FN=split("/",$0);
- my $SPID="/var/run/".$FN[-1];
- my $script_name= basename($0);
- my $cfg_file = "/usr/local/etc/backup.conf";
- my $ve_id = "";
- my $ve_root = "";
- if ($ARGV[0]) { $cfg_file = $ARGV[0]; }
- if ($ARGV[1]) { $ve_id = $ARGV[1]; }
- eval {
- #set timeout for script work
- $SIG{ALRM} = sub { die "Maximum worktime $WAIT_TIME sec reached!\n" };
- alarm $WAIT_TIME;
- if (! -e $cfg_file) { die ("File $cfg_file not found. Die...\n"); }
- require $cfg_file;
- my $filename = "";
- my $filefrom = "";
- my $tm = localtime;
- ($year,$month,$day,$wday) = ($tm->year+1900,$tm->mon+1,$tm->mday,$tm->wday);
- my $date = sprintf("%04d",$year).sprintf("%02d",$month).sprintf("%02d",$day);
- my $ttop = time;
- my $empty = 0;
- @dirlist = ();
- @exclist = ();
- *name = *File::Find::name;
- if (!($cfg_tmp_dir ne "" and $cfg_hostname ne "" and $cfg_ftp_hostname ne ""
- and $cfg_ftp_username ne "" and $cfg_ftp_password ne "" and $cfg_ftp_path
- ne "" and $cfg_full_day ne "" and $cfg_del_files ne "")) {
- die ("Not set configuration variables! Die...\n");
- }
- if ($cfg_admin_email eq "" or $cfg_from_email eq "") { $cfg_admin_email = "root"; $cfg_from_email = $cfg_admin_email; }
- if (!$cfg_use_ftp) { $cfg_use_ftp = "no"; }
- if (!$cfg_use_smb) { $cfg_use_smb = "no"; }
- if (!$tar_opts) { $tar_opts="--no-recursion"; }
- if (IsNotRun($SPID)) { Add_PID($SPID); }
- else { die ("Warning!!! backup.pl already runnning!\n"); }
- if (!$cfg_dirlist_file) { $cfg_dirlist_file ="/usr/local/etc/dirlist.conf"; }
- if (!$cfg_exclist_file) { $cfg_exclist_file ="/usr/local/etc/exclist.conf"; }
- if (!$cfg_mysql_dump) { $cfg_mysql_dump = "mysqldump"; }
- #for vz container
- if ($ve_id) {
- $cfg_exclist_file=~s/.conf//;
- $cfg_exclist_path=$cfg_exclist_file;
- #if found personal rule - use it
- if (-e $cfg_exclist_path."-$ve_id.conf") { $cfg_exclist_file=$cfg_exclist_path."-$ve_id.conf"; }
- else {
- #use common rule
- if (-e $cfg_exclist_path."-vz.conf") { $cfg_exclist_file=$cfg_exclist_path."-vz.conf"; }
- else {
- #if personal and common file not found - use default
- $cfg_exclist_file=$cfg_exclist_path.".conf";
- }
- }
- $ve_root = "/vz/root/$ve_id/";
- $cfg_hostname=$ve_id;
- }
- if (-e $cfg_dirlist_file) {
- open (FD,"<",$cfg_dirlist_file);
- while (<FD>) {
- chomp;
- push(@dirlist,$_);
- }
- close(FD);
- die ("Warning!!! no listing dir for backup. Exit.\n") if ($#dirlist eq "-1");
- }
- else { die ("File $cfg_dirlist_file not found! die...\n"); }
- if (-e $cfg_exclist_file) {
- open (FD1,"<",$cfg_exclist_file);
- while (<FD1>) {
- chomp;
- my $ex=$_;
- if ($ve_id) {
- if ($ex=~/\//) {
- if ($ex=~/\^(.*)/) {$ex="^".$ve_root.$1; } else { $ex=$ve_root.$ex; }
- $ex=~s/\/\//\//g;
- }
- }
- push(@exclist,$ex);
- }
- close(FD1);
- }
- #dirlist ignored if backup vz container
- #Left for compatibility
- if ($ve_id) { @dirlist = ( "./" ); }
- my $fullbackup = ($cfg_full_day eq $wday);
- if ((!$fullbackup) and (! -e "$cfg_status_dir/$cfg_hostname-back_file_size$ve_id.list")) { $fullbackup=1; }
- if (!$fullbackup) {
- open (FHD,">","$cfg_status_dir/$cfg_hostname-back_file_diff$ve_id.list");
- sub process_file_diff {
- if ($#exclist eq "-1") {
- if (-f) {
- $info = stat($name);
- print FHD $info->mtime." ".$info->size." ".$name."\n";
- }
- }else{
- if (-f) {
- $exists = 0;
- $info = stat($name);
- for ($i = 0; $i < @exclist; $i++) {
- $ii = $exclist[$i];
- $exists = ($exists or ($name =~ /$ii/));
- }
- if (!$exists) {
- print FHD $info->mtime." ".$info->size." ".$name."\n";
- }
- }
- }
- }
- if ($ve_root) { find(\&process_file_diff, $ve_root); }
- else {
- foreach $idir (@dirlist) {
- find(\&process_file_diff, $idir);
- }
- }
- close(FHD);
- system("diff -f $cfg_status_dir/$cfg_hostname-back_file_size$ve_id.list $cfg_status_dir/$cfg_hostname-back_file_diff$ve_id.list | grep / > $cfg_status_dir/$cfg_hostname-file$ve_id.diff");
- tie @ddiff, Tie::File, "$cfg_status_dir/$cfg_hostname-file$ve_id.diff";
- if ($#ddiff ne "-1") {
- open(FDD,">","$cfg_status_dir/$cfg_hostname-back_diff$ve_id.list");
- foreach $mdiff (@ddiff) {
- ($a,$b,$c) = split /\t/,$mdiff;
- $a = "";
- $b = "";
- print FDD $c."\n";
- }
- close (FDD);
- $filename = "$cfg_tmp_dir/$cfg_hostname-inc-$wday.tar.gz";
- $filefrom = "$cfg_status_dir/$cfg_hostname-back_diff$ve_id.list";
- }
- else { $empty=1; }
- }
- else{
- unlink <$cfg_tmp_dir/$cfg_hostname*>;
- open (FH1,">","$cfg_status_dir/$cfg_hostname-back_file_size$ve_id.list");
- open (FH2,">","$cfg_status_dir/$cfg_hostname-back_file$ve_id.list");
- sub process_file {
- if ($#exclist eq "-1") {
- if (-f) {
- $info = stat($name);
- print FH1 $info->mtime." ".$info->size." ".$name."\n";
- print FH2 $name."\n";
- }
- }else{
- if (-f) {
- $exists = 0;
- for ($i = 0; $i < @exclist; $i++) {
- $ii = $exclist[$i];
- $exists = ($exists or ($name =~ /$ii/));
- }
- if (!$exists) {
- $info = stat($name);
- print FH1 $info->mtime." ".$info->size." ".$name."\n";
- print FH2 $name."\n";
- }
- }
- }
- }
- if ($ve_root) { find(\&process_file, $ve_root); }
- else {
- foreach $idir (@dirlist) {
- find(\&process_file, $idir);
- }
- }
- close(FH1);
- close(FH2);
- $filename = "$cfg_tmp_dir/$cfg_hostname-full-$date.tar.gz";
- $filefrom = "$cfg_status_dir/$cfg_hostname-back_file$ve_id.list";
- }
- if ($empty eq 0) {
- my $ret=`tar $tar_opts -czPf $filename --files-from=$filefrom`;
- $tbot = time - $ttop;
- open(FHR,">","$cfg_status_dir/report$ve_id.txt");
- $minfo = stat($filename);
- print FHR sprintf("%-45s %10s %20s\n",basename($filename),$tbot,$minfo->size);
- close(FHR);
- }
- my @mysql_backup_files = ();
- my @mongo_backup_files = ();
- my @pgsql_backup_files = ();
- if ($cfg_mysql_backup eq "yes") {
- if ($cfg_mysql_db[0]=~/all/i) {
- my $run_cmd="echo 'show databases;' | mysql -u \"$cfg_mysql_user\" -h \"$cfg_mysql_host\" -s --password=\"$cfg_mysql_pass\"";
- if ($cfg_mysql_host!~/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/) {
- $run_cmd = "echo 'show databases;' | mysql -u \"$cfg_mysql_user\" -S \"$cfg_mysql_host\" -s --password=\"$cfg_mysql_pass\"";
- }
- @cfg_mysql_db = `$run_cmd`;
- chomp(@cfg_mysql_db);
- }
- $mysql_path = $0;
- $mysql_path=~ s/$script_name$/mysqld_backup/;
- $mysql_path=$mysql_path."2" if ($cfg_mysql_dump eq "mysqldump");
- foreach my $db (@cfg_mysql_db) {
- next if ($db=~/^information_schema$/);
- $ttop = time;
- my $lang='';
- if ($cfg_mysql_locale) { $lang="LANG=".$cfg_mysql_locale." "; }
- $fname="$cfg_tmp_dir/$cfg_hostname-mysql-$date-$db".".tgz";
- $ret=`$lang$mysql_path \"$cfg_mysql_host\" \"$cfg_mysql_user\" \"$cfg_mysql_pass\" \"$db\" \"$cfg_tmp_dir/$cfg_hostname-mysql-$date\"`;
- push (@mysql_backup_files,$fname) if ($? eq 0);
- $tbot = time - $ttop;
- open(FHR,">>","$cfg_status_dir/report$ve_id.txt");
- $minfo = stat($fname);
- print FHR sprintf("%-45s %10s %20s\n",$fname,$tbot,$minfo->size);
- close(FHR);
- }
- }
- if ($cfg_mongo_backup eq "yes") {
- $mongo_path = $0;
- $mongo_path=~ s/$script_name$/mongo_backup/;
- if ($cfg_mysql_db[0]=~/all/i) {
- $ttop = time;
- $fname="$cfg_tmp_dir/$cfg_hostname-mongo-$date-all".".tgz";
- my $ret=`$mongo_path \"$cfg_mongo_host\" \"$cfg_mongo_user\" \"$cfg_mongo_pass\" \"all\" \"$cfg_tmp_dir/$cfg_hostname-mongo-$date\"`;
- push (@mongo_backup_files,$fname) if ($? eq 0);
- $tbot = time - $ttop;
- open(FHR,">>","$cfg_status_dir/report$ve_id.txt");
- $minfo = stat($fname);
- print FHR sprintf("%-45s %10s %20s\n",$fname,$tbot,$minfo->size);
- close(FHR);
- } else {
- foreach my $db (@cfg_mongo_db) {
- $ttop = time;
- $fname="$cfg_tmp_dir/$cfg_hostname-mongo-$date-$db".".tgz";
- my $ret=`$mongo_path \"$cfg_mongo_host\" \"$cfg_mongo_user\" \"$cfg_mongo_pass\" \"$db\" \"$cfg_tmp_dir/$cfg_hostname-mongo-$date\"`;
- push (@mongo_backup_files,$fname) if ($? eq 0);
- $tbot = time - $ttop;
- open(FHR,">>","$cfg_status_dir/report$ve_id.txt");
- $minfo = stat($fname);
- print FHR sprintf("%-45s %10s %20s\n",$fname,$tbot,$minfo->size);
- close(FHR);
- }
- }
- }
- if ($cfg_pgsql_backup eq "yes") {
- $cfg_pgsql_su = "no" if (!$cfg_pgsql_su);
- open(PGP,">","/root/.pgpass");
- print PGP "$cfg_pgsql_host:5432:*:$cfg_pgsql_user:$cfg_pgsql_pass";
- close(PGP);
- chmod 0400,"/root/.pgpass";
- chown 0,0,"/root/.pgpass";
- if ($cfg_pgsql_dump eq "pg_dump") {
- $pgsql_path = $0;
- $pgsql_path=~ s/$script_name$/postgres_listdb/;
- if ($cfg_pgsql_db[0]=~/all/i) {
- @cfg_pgsql_db = `$lang$pgsql_path \"$cfg_pgsql_su\" \"$cfg_pgsql_host\" \"$cfg_pgsql_user\"`;
- chomp(@cfg_pgsql_db);
- }
- $pgsql_path = $0;
- $pgsql_path=~ s/$script_name$/postgres_backup1/;
- foreach my $db (@cfg_pgsql_db) {
- next if (!$db);
- my $lang='';
- $ttop = time;
- if ($cfg_pgsql_locale) { $lang="LANG=".$cfg_pgsql_locale." "; }
- $fname = "$cfg_tmp_dir/$cfg_hostname-pgsql-$date-$db".".tgz";
- $ret=`$lang$pgsql_path \"$db\" \"$cfg_tmp_dir/$cfg_hostname-pgsql-$date\" \"$cfg_pgsql_su\" \"$cfg_pgsql_host\" \"$cfg_pgsql_user\"`;
- push (@pgsql_backup_files,$fname) if ($? eq 0);
- $tbot = time - $ttop;
- open(FHR,">>","$cfg_status_dir/report$ve_id.txt");
- $minfo = stat($fname);
- print FHR sprintf("%-45s %10s %20s\n",$fname,$tbot,$minfo->size);
- close(FHR);
- }
- } else {
- $pgsql_path = $0;
- $pgsql_path=~ s/$script_name$/postgres_backup2/;
- my $lang='';
- $ttop = time;
- if ($cfg_pgsql_locale) { $lang="LANG=".$cfg_pgsql_locale." "; }
- $fname = "$cfg_tmp_dir/$cfg_hostname-pgsql-$date-$cfg_hostname".".tgz";
- $ret=`$lang$pgsql_path \"$cfg_tmp_dir/$cfg_hostname-pgsql-$date\" \"$cfg_pgsql_su\" \"$cfg_pgsql_user\" \"$cfg_pgsql_host\" \"$cfg_hostname\"`;
- push (@pgsql_backup_files,$fname) if ($? eq 0);
- $tbot = time - $ttop;
- open(FHR,">>","$cfg_status_dir/report$ve_id.txt");
- $minfo = stat($fname);
- print FHR sprintf("%-45s %10s %20s\n",$fname,$tbot,$minfo->size);
- close(FHR);
- }
- unlink "/root/.pgpass";
- }
- if ($cfg_use_ftp ne "no") {
- $ftp = Net::FTP->new($cfg_ftp_hostname,Timeout => 30,Passive => $cfg_ftp_mode);
- die("Can't connect to $cfg_ftp_hostname !\n") if (!$ftp);
- die ("Couldn't authenticate, even with explicit username and password.\n") if (!$ftp->login($cfg_ftp_username,$cfg_ftp_password));
- if (!$ftp->cwd($cfg_ftp_path)) {
- if ($ftp->mkdir ($cfg_ftp_path,1)) { $ftp->cwd($cfg_ftp_path); }
- else { die ("Can't create directory $cfg_ftp_path at $cfg_ftp_hostname! Die...\n"); }
- }
- $ftp->binary();
- if ($filename) {
- die ("Can't put backup $filename to $cfg_ftp_hostname!\n") if (!$ftp->put($filename));
- }
- foreach $db (@mysql_backup_files) {
- die ("Can't put $db to $cfg_ftp_hostname!\n") if (!$ftp->put("$db"));
- }
- foreach $db (@pgsql_backup_files) {
- die ("Can't put $db to $cfg_ftp_hostname!\n") if (!$ftp->put("$db"));
- }
- $ftp->put("$cfg_status_dir/report$ve_id.txt");
- $ftp->quit();
- };
- if ($cfg_use_smb ne "no") {
- my $smb_path=$0;
- $smb_path=~ s/$script_name$/smb_copy/;
- my $ret=`$smb_path \"$cfg_smb_hostname\" \"$cfg_smb_username\" \"$cfg_smb_password\" \"$cfg_smb_share\" \"$cfg_smb_path\" \"$filename\"`;
- my $res = $?;
- die "$ret" if ($res);
- foreach $db (@mysql_backup_files) {
- `$smb_path \"$cfg_smb_hostname\" \"$cfg_smb_username\" \"$cfg_smb_password\" \"$cfg_smb_share\" \"$cfg_smb_path\" \"$db\"`;
- $res = $?;
- die "$ret" if ($res);
- }
- foreach $db (@mongo_backup_files) {
- die ("Can't put $db to $cfg_ftp_hostname!\n") if (!$ftp->put("$db"));
- }
- foreach $db (@pgsql_backup_files) {
- `$smb_path \"$cfg_smb_hostname\" \"$cfg_smb_username\" \"$cfg_smb_password\" \"$cfg_smb_share\" \"$cfg_smb_path\" \"$db\"`;
- $res = $?;
- die "$ret" if ($res);
- }
- }
- unlink <$cfg_tmp_dir/$cfg_hostname*> if ($cfg_del_files eq "yes");
- $SIG{ALRM} = 'DEFAULT';
- };
- if ($@) { abort($cfg_admin_email,$cfg_from_email,"Script aborted. Error: $@\n",$SPID); };
- if (IsMyPID($SPID)) { Remove_PID($SPID); };
- exit 0;
- #---------------------------------------------------------------------------------------------------------
- sub sendEmail
- {
- my ($to, $from, $subject, $message) = @_;
- my $sendmail = '/usr/lib/sendmail';
- open(MAIL, "|$sendmail -oi -t");
- print MAIL "From: $from\n";
- print MAIL "To: $to\n";
- print MAIL "Subject: $subject\n\n";
- print MAIL "$message\n";
- close(MAIL);
- }
- #---------------------------------------------------------------------------------------------------------
- sub abort {
- my ($to,$from,$message, $pid) = @_;
- sendEmail ($to,$from,"Backup error at $cfg_hostname!",$message);
- if (IsMyPID($pid)) { Remove_PID($pid); };
- die ($message);
- }
- #---------------------------------------------------------------------------------------------------------
- sub IsNotRun {
- my $pname = shift;
- my $lockfile = $pname.".pid";
- if (! -e $lockfile) { return 1; }
- open (FF,"<$lockfile") or die "can't open file $lockfile: $!";
- my $lockid = <FF>;
- close(FF);
- if ($lockid eq $$) { return 1; }
- my $process_count = `ps axwu | awk '\{ print \$2 \}' | grep $lockid | wc -l`;
- if ($process_count lt 1) { unlink $lockfile; return 1; }
- return 0;
- }
- #---------------------------------------------------------------------------------------------------------
- sub IsMyPID {
- my $pname = shift;
- my $lockfile = $pname.".pid";
- if (! -e $lockfile) { return 0; }
- open (FF,"<$lockfile") or die "can't open file $lockfile: $!";
- my $lockid = <FF>;
- close(FF);
- if ($lockid eq $$) { return 1; }
- return 0;
- }
- #---------------------------------------------------------------------------------------------------------
- sub Add_PID {
- my $pname = shift;
- my $lockfile = $pname.".pid";
- open (FF,">$lockfile") or die "can't open file $lockfile: $!";
- flock(FF,2) or die "can't flock $lockfile: $!";
- print FF $$;
- close(FF);
- return 1;
- }
- #---------------------------------------------------------------------------------------------------------
- sub Remove_PID {
- my $pname = shift;
- my $lockfile = $pname.".pid";
- if (! -e $lockfile) { return 1; }
- unlink $lockfile or return 0;
- return 1;
- }
|