131 lines
4.0 KiB
Perl
Executable File
131 lines
4.0 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
use warnings;
|
|
use strict;
|
|
use Simba::CA;
|
|
use POSIX qw(strftime);
|
|
use Getopt::Long;
|
|
use Filesys::Statvfs;
|
|
use File::stat;
|
|
|
|
my @filesets;
|
|
|
|
my $parallel;
|
|
GetOptions(
|
|
'filesets=i' => \@filesets,
|
|
'parallel=i' => \$parallel
|
|
);
|
|
@filesets = split(/,/,join(',',@filesets));
|
|
|
|
$ENV{PATH} = "/usr/bin";
|
|
|
|
my $now = strftime('%Y-%m-%dT%H:%M:%S', localtime());
|
|
open(my $log, '>>', '/var/log/simba/ca.log.' . $now);
|
|
$log->autoflush(1);
|
|
|
|
my $ca = Simba::CA->new({
|
|
dbi_file => $ENV{SIMBA_DB_CONN} || "$ENV{HOME}/.dbi/simba",
|
|
fh_log => $log,
|
|
(@filesets ? ( filesets => \@filesets ) : ()),
|
|
parallel => $parallel,
|
|
});
|
|
$ca->log_level(9);
|
|
|
|
# Try to find all devices suitable for backup and mount them.
|
|
# We do this by trying to find a matching device for each subdirectory
|
|
# of /backup. Another way might be to check all USB disks.
|
|
my $st = stat("/backup/");
|
|
my $base_device = $st->dev;
|
|
my %luks_devices;
|
|
for (glob("/backup/*")) {
|
|
my $st = stat($_);
|
|
my $dir_device = $st->dev;
|
|
$ca->log(0, "checking $_");
|
|
if ($base_device == $dir_device) {
|
|
# not a mount point
|
|
(my $basedir = $_) =~ s{^/backup/}{};
|
|
if ($basedir =~ /^luks-(.*)/) {
|
|
my $key = $1;
|
|
for my $dev (glob("/dev/disk/by-id/*$key*")) {
|
|
my ($devbase) = $dev =~ m{([^/]+$)};
|
|
if (-e "/backup/keys/$devbase") {
|
|
$ca->log(0, "opening /dev/disk/by-id/$devbase on $_");
|
|
system("/sbin/cryptsetup", "open", $dev, $basedir, "--key-file", "/backup/keys/$devbase");
|
|
$ca->log(0, "mounting /dev/mapper/$basedir on $_");
|
|
system("/bin/mount", "-o", "nodev,noexec,nomand,nosuid", "/dev/mapper/$basedir", $_);
|
|
}
|
|
}
|
|
} elsif (-e "/dev/disk/by-id/$basedir") {
|
|
# matching device exists
|
|
$ca->log(0, "mounting /dev/disk/by-id/$basedir on $_");
|
|
system("/bin/mount", "-o", "nodev,noexec,nomand,nosuid", "/dev/disk/by-id/$basedir", $_);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Choose a random backup directory.
|
|
# The directories are weighted by free space (e.g, if we have two
|
|
# directories with 3 TB and 2 TB free space, then their chances of being
|
|
# chosen are 60% and 40%).
|
|
|
|
my @backup_dirs = map {
|
|
$_ = $1 if m{(.*)/.*}; # basedir and detaint
|
|
my($bsize, $frsize, $blocks, $bfree, $bavail,
|
|
$files, $ffree, $favail, $flag, $namemax)
|
|
= statvfs($_);
|
|
my $available_bytes = $bsize * $bavail;
|
|
my $avg_file_size = $bavail * ($blocks - $bfree) / ($files - $ffree);
|
|
my $available_bytes_by_files = $avg_file_size * $favail;
|
|
$available_bytes = $available_bytes_by_files if $available_bytes_by_files < $available_bytes;
|
|
$ca->log(3, "found base $_ (est. $available_bytes bytes)");
|
|
[ $_, $available_bytes ]
|
|
} glob("/backup/*/active");
|
|
my $sum_free = 0;
|
|
$sum_free += $_->[1] for (@backup_dirs);
|
|
$ca->log(3, "total free est. $sum_free bytes");
|
|
my $rnd = rand();
|
|
$ca->log(3, "random (raw) = $rnd");
|
|
$rnd *= $sum_free;
|
|
$ca->log(3, "random (scaled) = $rnd");
|
|
my $count_free = 0;
|
|
my $backup_dir;
|
|
for(@backup_dirs) {
|
|
$count_free += $_->[1];
|
|
$ca->log(3, "considering base $_->[0] (est. $_->[1] bytes)");
|
|
if ($count_free >= $rnd) {
|
|
$backup_dir = $_->[0];
|
|
$ca->log(3, "using base $_->[0]");
|
|
last;
|
|
}
|
|
}
|
|
unless ($backup_dir) {
|
|
$ca->log(0, "no backup directory found");
|
|
exit(1);
|
|
}
|
|
$ca->basedir($backup_dir);
|
|
|
|
# umount all potential backup dirs again, except the one we are actually
|
|
# using
|
|
for (@backup_dirs) {
|
|
next if $_->[0] eq $backup_dir;
|
|
$ca->log(0, "unmounting $_->[0]");
|
|
system("/bin/umount", $_->[0]);
|
|
if ($_->[0] =~ m{(luks-[^/]+)}) {
|
|
$ca->log(0, "closing $1");
|
|
system("/sbin/cryptsetup", "close", $1)
|
|
}
|
|
}
|
|
chdir($backup_dir); # prevent accidental umount
|
|
|
|
$ca->run();
|
|
|
|
# umount backup dir
|
|
|
|
chdir("/");
|
|
$ca->log(0, "unmounting $backup_dir");
|
|
system("/bin/umount", $backup_dir);
|
|
if ($backup_dir =~ m{(luks-[^/]+)}) {
|
|
$ca->log(0, "closing $1");
|
|
system("/sbin/cryptsetup", "close", $1)
|
|
}
|