#!/usr/bin/perl
################################################################################ rt - restic wrapper (mwx'2026)
use File::Basename;use Term::ReadKey;use Getopt::Std;getopts('c:n:i:t:p:');

$VERSION='1.5.6';

$CONFIG='default';$CONFIG=$opt_c if ($opt_c!~/^\s*$/);                             # set deault options options
chomp($host=`hostname`);$host=~s/^([^\.]+).*/$1/;
$NAME=$host;
$POLICY='--keep-daily 14 --keep-weekly 12 --keep-monthly 12 --keep-yearly 100';

for ('/db/etc/rt.conf',$ENV{"HOME"}.'/.rt.conf') { $CONF=$_ if (-f $_);}        # find/load/check configuration
die "Fatal: no config file found\n" if ($CONF=~/^\s*$/);

require($CONF);    

$NAME     = $opt_n if ($opt_n!~/^\s*$/);         # overwrite options with commandline (name, id, tag, password) 
$ID       = $opt_i if ($opt_i!~/^\s*$/); 
$TAG      = $opt_t if ($opt_t!~/^\s*$/); 
$PASSWORD = $opt_p if ($opt_p!~/^\s*$/); 

for ('$NAME','$TARGET','$SRC','$RESTIC','$LOG') {
  die "Fatal: missing $_ in config\n" if (eval($_)=~/^\s*$/);
}

$ID='0' if ($ID=~/^\s*$/);                                                                     # set repository
$REPOSITORY="$TARGET/$NAME.$ID";


print "rt $VERSION, Name: $NAME, Repository: $REPOSITORY, Config: $CONFIG\n";


open(PW,"> $PWFILE");print PW "$PASSWORD\n";close(PW);

$ENV{'RESTIC_REPOSITORY'}    = $REPOSITORY;                                                   # set environment
$ENV{'RESTIC_PASSWORD_FILE'} = $PWFILE;

$r=`$RESTIC version`;                                                                     # find restic version
if ($r=~/restic\s+([\d\.]+)\s+compiled/) {
  $RVERSION=$1;
  @RV=split('\.',$1);$RVN=sprintf("%02d%02d%02d",$RV[0],$RV[1],$RV[2]);
}

$TMP="/tmp";

my(@z)=localtime(time());$z[4]+=1;$z[5]-=100;$z[6]+=1;
$z=sprintf("%02d\.%02d\.%02d %02d\:%02d",$z[3],$z[4],$z[5],$z[2],$z[1]);


if ($ARGV[0] eq 'cleanup') { # ----------------------------------------------------------- forget, prune, check
  
  $ts=time();

  print "$RESTIC -r $REPOSITORY forget $POLICY --prune\n";                                     # forget & prune

  $rms=0;
  open(IN,"$RESTIC forget $POLICY --prune |");
  while(<IN>) {
    print;
    $rms=$1 if (/^(\d+)\s+snapshots\s+have\s+been\s+removed/) 
  }
  
  print "$RESTIC -r $REPOSITORY check\n";                                                               # check
    
  open(IN,"$RESTIC check |");
  while(<IN>) {
    print;
    chomp($lastline=$_);
  }
  
  $rt=time()-$ts;
  
  if ($HTTP_LOG!~/^\s*$/) {                                                                     # http loging
    $tmp= "RTC,$VERSION/$RVERSION,$NAME,$ID,$CONFIG,$TAG,$REPOSITORY,$lastline,$rms,$rt";
  	use LWP::UserAgent;
		my $ua = new LWP::UserAgent();
		$response = $ua->post("https://$HTTP_LOG", Content => $tmp);
	}
  
  if ($MY_HOST!~/^\s*$/) {                                                                     # mysql loging
    use DBI; 
    $dbh=DBI->connect("DBI:mysql:$MY_DB:$MY_HOST:$MY_PORT",$MY_USER,$MY_PASSWD,{RaiseError=>1,PrintError=>1});
    $sql="INSERT INTO resticcleanup VALUES(now(),'$VERSION/$RVERSION',
      '$NAME','$ID','$CONFIG','$TAG','$REPOSITORY','$lastline',$rms,$rt)";
    $dbh->do($sql);
  }
  
} elsif  ($ARGV[0] eq 'backup' || $ARGV[0] eq 'delaybackup') { # ----------------------------------- run backup
  $snap='';$added='';$total='';

  if ($ARGV[0] eq 'delaybackup') {
    printf "$z --- delaybackup (%d sec) ---\n",&dfn($NAME);
    sleep(&dfn($NAME))
  } else {
    printf "$z --- backup ---\n";
  }

  print "$RESTIC -r $REPOSITORY -vv backup $EXCLUDE $SRC\n";
  
  $ts=time();
  open(IN,"$RESTIC -vv backup $EXCLUDE $SRC |");
  while(<IN>) {
    print if (!/^unchanged/);
  
    if (/Files:\s*(\d+)\s*new,\s*(\d+)\s*changed,\s*(\d+)\s*unmodified/) {
      $fn+=$1;$fc+=$2;$fu+=$3;  
    }
    if (/Dirs:\s*(\d+)\s*new,\s*(\d+)\s*changed,\s*(\d+)\s*unmodified/) {
      $fn+=$1;$fc+=$2;$fu+=$3;  
    }
    $snap=$1 if (/snapshot\s+([^\s]+)\s+saved/); 
    $added=&deform($1) if (/Added\s+to\s+the\s+repo:\s+(.*)/);
    $added=&deform($1) if (/Added\s+to\s+the\s+repository:\s+([^\(]*)/);

    $total=&deform($1) if (/processed\s+\d+\s+files,\s+(.*)\s+in/);
    $files=$1 if (/processed\s+(\d+)\s+files,\s+.*\s+in/);
  }

  if ($snap!~/^\s*$/ && $added!~/^\s*$/ && $total!~/^\s*$/) {
    $rt=time()-$ts;
    
    open(LOG,">> $LOG");                                                                        # write logfile
    print LOG "$z [$NAME/$snap], Files: $files, New: $fn, Changed: $fc, ".
              "Unmodified: $fu, Added: $added, Total: $total, Runtime: $rt\n";
    close(LOG);
    
    if ($HTTP_LOG!~/^\s*$/) {                                                                     # http loging
      $tmp= "RT,$VERSION/$RVERSION,$NAME,$ID,$CONFIG,$TAG,$REPOSITORY,".
             "$snap,$files,$fn,$fc,$fu,$added,$total,$rt";
			use LWP::UserAgent;
			my $ua = new LWP::UserAgent();
			$response = $ua->post("https://$HTTP_LOG", Content => $tmp);
		}

    if ($MY_HOST!~/^\s*$/) {                                                                     # mysql loging
      use DBI; 
      $dbh=DBI->connect("DBI:mysql:$MY_DB:$MY_HOST:$MY_PORT",$MY_USER,$MY_PASSWD,{RaiseError=>1,PrintError=>1});
      $sql="INSERT INTO restic VALUES(now(),'$VERSION/$RVERSION',
        '$NAME','$ID','$CONFIG','$TAG','$REPOSITORY','$snap',$files,$fn,$fc,$fu,$added,$total,$rt)";
      $dbh->do($sql);
    }
  }


} elsif ($ARGV[0] eq 'password') { # ----------------------------------------------- setup restic password file  
  system("mkdir -p ".&dirname($PWFILE)) if (!-d &dirname($PWFILE));
  
  print "new repository password: ";
  chomp($pwd = &ReadLine(0));

  if ($pwd!~/^\s*$/) {
    open(OUT,"> $PWFILE");print OUT "$pwd\n";close(OUT);
  }
      
      
} elsif ($ARGV[0] eq 'help' || $ARGV[0]=~/^\s*$/) { # ----------------------------------------------- show help
  
  &help();
  &advhelp() if ($ARGV[0] eq 'help');
  

} else { # ------------------------------------------------------------------------------ other restic commands
  
  system("$RESTIC @ARGV"); 
  
  if ($ARGV[0] eq 'init') {
    system("rm -f /var/log/restic.log /var/log/rt.log");
  }

}

####################################################################################################### SUPPORT

sub deform() {                                                                                # transform sizes
  my($s)=@_;
  return $1<<40 if ($s=~/([\d\.]+)\s*TiB/i);
  return $1<<30 if ($s=~/([\d\.]+)\s*GiB/i);
  return $1<<20 if ($s=~/([\d\.]+)\s*MiB/i);
  return $1<<10 if ($s=~/([\d\.]+)\s*KiB/i);
  return $1     if ($s=~/([\d\.]+)\s*B/i);
}

sub dfn() {                                                                       # calculate delay from string 
  my($s)=@_;my $v=0;my $n=1;
  $v+=ord($_)*$n++ for (split('',$s));
  return ($v%24)*5*60;
}

sub help() {                                                                                             # help
print "\nrt v$VERSION - restic wrapper (mwx'2022)\n";
print <<'ENDOFHELP';

  USAGE:
  ------ 
    rt [options] backup       run restic backup
    rt [options] delaybackup  run delayed restic backup 
    rt [options] cleanup      run forget, prune and check 
    rt [options] password     setup restic password file
    rt [options] init         initialize restic repository
    rt help                   advanced help
    
    rt <command>              other commands will be forwarded directly to restic         

    options: -c <config>      sets $CONFIG (default is 'default')
             -n <name>        overwrite $NAME
             -i <id>          overwrite $ID
             -t <tag>         overwrite $TAG
             -p <password>    overwrite $PASSWORD 
              
ENDOFHELP
}

sub advhelp() {                                                                                 # advanced help
print <<'ENDOFHELP';
  CONFIG FILE (/db/etc/rt.conf)
  -----------------------------
    $NAME      = 'NAME';                                       # backup name
    $ID        = '0';                                          # repository id (optional, default is 0)
    $TAG       = 'TAG';                                        # custom tag for logging (optional)
  
    $TARGET    = "/backup/restic";                             # restic target 
               = "s3:http://188.68.34.116:9234/restic";
               = "b2:mwx002:restic/server";
  
    $SRC       = '/etc /db /var /root /home /usr/local /opt';  # source paths
  
    $EXCLUDE   = '-e /var/lib/lxcfs -e /var/log/journal';      # exclude options
  
    $RESTIC    = '/db/bin/restic';                             # restic binary
    $LOG       = '/var/log/restic.log';                        # log file
    $PWFILE    = '/db/etc/restic.pw';                          # restic password file

    $HTTP_LOG  = 'HOST';                                       # host for logging via https
  
    $MY_HOST   = 'HOST';                                       # mysql host
    $MY_PORT   = 'PORT';                                       # mysql port
    $MY_USER   = 'USER';                                       # mysql user 
    $MY_PASSWD = 'PASSWORD';                                   # mysql password
    $MY_DB     = 'DATABASE';                                   # mysql database


    $ENV{'AWS_ACCESS_KEY_ID'}     = '123456';                  # S3 token example
    $ENV{'AWS_SECRET_ACCESS_KEY'} = 'abcdefg';

    $ENV{'B2_ACCOUNT_ID'}         = '123456';                  # B2 token example
    $ENV{'B2_ACCOUNT_KEY'}        = 'abcdefg';

  MYSQL LOGGING TABLES:
  ---------------------
    CREATE TABLE `restic` (
      `ts` timestamp NULL DEFAULT NULL,
      `version` varchar(255) DEFAULT NULL,
      `name` varchar(255) DEFAULT NULL,
      `id` int(11) DEFAULT NULL,
      `config` varchar(255) DEFAULT NULL,
      `tag` varchar(255) DEFAULT NULL,
      `repository` varchar(255) DEFAULT NULL,
      `snapshot` varchar(255) DEFAULT NULL,
      `files` bigint(11) DEFAULT NULL,
      `new` bigint(11) DEFAULT NULL,
      `changed` bigint(11) DEFAULT NULL,
      `unmodified` bigint(11) DEFAULT NULL,
      `added` bigint(11) DEFAULT NULL,
      `total` bigint(11) DEFAULT NULL,
      `runtime` int(11) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    
    CREATE TABLE `resticcleanup` (
      `ts` timestamp NULL DEFAULT NULL,
      `version` varchar(255) DEFAULT NULL,
      `name` varchar(255) DEFAULT NULL,
      `id` int(11) DEFAULT NULL,
      `config` varchar(255) DEFAULT NULL,
      `tag` varchar(255) DEFAULT NULL,
      `repository` varchar(255) DEFAULT NULL,
      `txt` varchar(255) DEFAULT NULL,
      `rmsnap` int(11) DEFAULT NULL,
      `runtime` int(11) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

  CRONTAB ENTRIES:
  ---------------
    0 2 * * * /db/bin/rt delaybackup >> /var/log/rt.log 2>&1
    0 7 * * 6 /db/bin/rt cleanup >> /var/log/rt.log 2>&1
    
ENDOFHELP
}

########################################################################################################### END
