#!/usr/local/bin/perl
# fsdump-lib.pl
# Common functions for doing filesystem backups with dump
BEGIN { push(@INC, ".."); };
use WebminCore;
&init_config();
if ($gconfig{'os_type'} =~ /^\S+\-linux$/) {
do "linux-lib.pl";
}
else {
do "$gconfig{'os_type'}-lib.pl";
}
&foreign_require("mount", "mount-lib.pl");
%access = &get_module_acl();
$cron_cmd = "$module_config_directory/backup.pl";
$newtape_cmd = "$module_config_directory/newtape.pl";
$notape_cmd = "$module_config_directory/notape.pl";
$multi_cmd = "$module_config_directory/multi.pl";
$rmulti_cmd = "$module_config_directory/rmulti.pl";
$ftp_cmd = "$module_config_directory/ftp.pl";
# list_dumps()
# Returns a list of all scheduled dumps
sub list_dumps
{
local (@rv, $f);
opendir(DIR, $module_config_directory);
foreach $f (sort { $a cmp $b } readdir(DIR)) {
next if ($f !~ /^(\S+)\.dump$/);
push(@rv, &get_dump($1));
}
closedir(DIR);
return @rv;
}
# get_dump(id)
sub get_dump
{
local %dump;
&read_file("$module_config_directory/$_[0].dump", \%dump) || return undef;
$dump{'id'} = $_[0];
return \%dump;
}
# save_dump(&dump)
sub save_dump
{
$_[0]->{'id'} = $$.time() if (!$_[0]->{'id'});
&lock_file("$module_config_directory/$_[0]->{'id'}.dump");
&write_file("$module_config_directory/$_[0]->{'id'}.dump", $_[0]);
&unlock_file("$module_config_directory/$_[0]->{'id'}.dump");
}
# delete_dump(&dump)
sub delete_dump
{
&lock_file("$module_config_directory/$_[0]->{'id'}.dump");
unlink("$module_config_directory/$_[0]->{'id'}.dump");
&unlock_file("$module_config_directory/$_[0]->{'id'}.dump");
}
# directory_filesystem(dir)
# Returns the filesystem type of some directory , or the full details
# if requesting an array
sub directory_filesystem
{
local $fs;
foreach my $m (sort { length($a->[0]) <=> length($b->[0]) }
&mount::list_mounted()) {
local $l = length($m->[0]);
if ($m->[0] eq $_[0] || $m->[0] eq "/" ||
(length($_[0]) >= $l && substr($_[0], 0, $l+1) eq $m->[0]."/")) {
$fs = $m;
}
}
return wantarray ? @$fs : $fs->[2];
}
# is_mount_point(dir)
# Returns 1 if some directory is a filesystem mount point
sub is_mount_point
{
local ($dir) = @_;
foreach my $m (&mount::list_mounted()) {
return 1 if ($m->[0] eq $dir);
}
return 0;
}
# same_filesystem(fs1, fs2)
# Returns 1 if type filesystem types are the same
sub same_filesystem
{
local ($fs1, $fs2) = @_;
$fs1 = "ext2" if ($fs1 eq "ext3");
$fs2 = "ext2" if ($fs2 eq "ext3");
return lc($fs1) eq lc($fs2);
}
# date_subs(string, [time])
sub date_subs
{
local ($path, $time) = @_;
local $rv;
if ($config{'date_subs'}) {
eval "use POSIX";
eval "use posix" if ($@);
local @tm = localtime($time || time());
&clear_time_locale();
$rv = strftime($path, @tm);
&reset_time_locale();
}
else {
$rv = $path;
}
if ($config{'webmin_subs'}) {
$rv = &substitute_template($rv, { });
}
return $rv;
}
# execute_before(&dump, handle, escape)
# Executes the before-dump command, and prints the output. Returns 1 on success
# or 0 on failure
sub execute_before
{
my ($dump, $h, $esc) = @_;
if ($dump->{'before'}) {
&set_dump_envs($dump);
&open_execute_command(before, "($dump->{'before'}) 2>&1 ) {
print $h $esc ? &html_escape($_) : $_;
}
close(before);
&reset_dump_envs();
return !$?;
}
return 1;
}
# execute_after(&dump, handle, escape)
# Executes the before-dump command, and prints the output. Returns 1 on success
# or 0 on failure
sub execute_after
{
my ($dump, $h, $esc) = @_;
if ($dump->{'after'}) {
&set_dump_envs($dump);
&open_execute_command(after, "($dump->{'after'}) 2>&1 ) {
print $h $esc ? &html_escape($_) : $_;
}
close(after);
&reset_dump_envs();
return !$?;
}
return 1;
}
# set_dump_envs(&dump)
# Sets FSDUMP_ environment variables based on attributes of the dump
sub set_dump_envs
{
my ($dump) = @_;
foreach my $k (keys %$dump) {
$ENV{'FSDUMP_'.uc($k)} = $dump->{$k};
}
}
# reset_dump_envs()
# Clear all variables set by set_dump_envs
sub reset_dump_envs
{
foreach my $k (keys %ENV) {
delete($ENV{$k}) if ($k =~ /^FSDUMP_/);
}
}
# running_dumps(&procs)
# Returns a list of backup jobs currently in progress, and their statuses
sub running_dumps
{
local ($p, @rv, %got);
foreach $p (@{$_[0]}) {
if (($p->{'args'} =~ /$cron_cmd\s+(\S+)/ ||
$p->{'args'} =~ /$module_root_directory\/backup.pl\s+(\S+)/) &&
$p->{'args'} !~ /^\/bin\/(sh|bash|csh|tcsh)/) {
local $backup = &get_dump($1);
local $sfile = "$module_config_directory/$1.$p->{'pid'}.status";
local %status;
if (&read_file($sfile, \%status)) {
$backup->{'status'} = \%status;
$backup->{'pid'} = $p->{'pid'};
push(@rv, $backup);
$got{$sfile} = 1 if (!$status{'end'});
}
}
}
# Remove any left over .status files
opendir(DIR, $module_config_directory);
local $f;
foreach $f (readdir(DIR)) {
local $path = "$module_config_directory/$f";
unlink($path) if ($path =~ /\.status$/ && !$got{$path});
}
closedir(DIR);
return @rv;
}
# can_edit_dir(dir)
# Returns 1 if some backup can be used or edited
sub can_edit_dir
{
return 1 if ($access{'dirs'} eq '*');
local ($d, $dd);
local @ddirs = !ref($_[0]) ? ( $_[0] ) :
$supports_multiple ? split(/\s+/, $_[0]->{'dir'}) :
( $_[0]->{'dir'} );
foreach $dd (@ddirs) {
local $anyok = 0;
foreach $d (split(/\t+/, $access{'dirs'})) {
$anyok = 1 if (&is_under_directory($d, $dd));
}
return 0 if (!$anyok);
}
return 1;
}
sub create_wrappers
{
&foreign_require("cron", "cron-lib.pl");
&cron::create_wrapper($notape_cmd, $module_name, "notape.pl");
&cron::create_wrapper($newtape_cmd, $module_name, "newtape.pl");
&cron::create_wrapper($multi_cmd, $module_name, "multi.pl");
&cron::create_wrapper($rmulti_cmd, $module_name, "rmulti.pl");
}
# new_header(title)
sub new_header
{
print "
\n";
print "
| $_[0] |