553 lines
13 KiB
Plaintext
553 lines
13 KiB
Plaintext
|
#!/usr/bin/perl -wT
|
||
|
#----------------------------------------------------------------------
|
||
|
# heading : Your Settings
|
||
|
# description : Data Backup
|
||
|
# longdesc : backup user data
|
||
|
# navigation : 100 200
|
||
|
#
|
||
|
# Copyright (c) 2001 Mitel Networks Corporation
|
||
|
# 2002-5 Stephen Noble <support@dungog.net>
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation; either version 2 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program; if not, write to the Free Software
|
||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
#----------------------------------------------------------------------
|
||
|
|
||
|
package esmith;
|
||
|
|
||
|
use strict;
|
||
|
use CGI ':all';
|
||
|
use CGI::Carp qw(fatalsToBrowser);
|
||
|
|
||
|
use esmith::cgi;
|
||
|
use esmith::config;
|
||
|
use esmith::util;
|
||
|
use esmith::db;
|
||
|
use esmith::lockfile;
|
||
|
|
||
|
sub showInitial ($$);
|
||
|
sub performAndShowResult ($);
|
||
|
sub desktopBackup ();
|
||
|
sub desktopRestore ();
|
||
|
sub desktopVerify ();
|
||
|
sub performDesktopRestore ($);
|
||
|
sub performDesktopVerify ($);
|
||
|
sub performDiskUsage ($);
|
||
|
|
||
|
BEGIN
|
||
|
{
|
||
|
# Clear PATH and related environment variables so that calls to
|
||
|
# external programs do not cause results to be tainted. See
|
||
|
# "perlsec" manual page for details.
|
||
|
|
||
|
$ENV {'PATH'} = '';
|
||
|
$ENV {'SHELL'} = '/bin/bash';
|
||
|
delete $ENV {'ENV'};
|
||
|
}
|
||
|
|
||
|
esmith::util::setRealToEffective ();
|
||
|
|
||
|
my %conf;
|
||
|
tie %conf, 'esmith::config';
|
||
|
|
||
|
my %accounts;
|
||
|
tie %accounts, 'esmith::config', '/home/e-smith/db/accounts';
|
||
|
|
||
|
my $acctName = $ENV{'REMOTE_USER'};
|
||
|
|
||
|
if ($acctName =~ /^([a-z][\-\_\.a-z0-9]*)$/)
|
||
|
{
|
||
|
$acctName = $1;
|
||
|
}
|
||
|
|
||
|
my @directories = ("/home/e-smith/files/users/$acctName/");
|
||
|
|
||
|
|
||
|
# Unbuffer standard output so that files and directories are listed as
|
||
|
# they are restored
|
||
|
$| = 1;
|
||
|
|
||
|
# Store away current gid of 'www' group.
|
||
|
my $www_gid = getgrnam("www");
|
||
|
|
||
|
#------------------------------------------------------------
|
||
|
# examine state parameter and display the appropriate form
|
||
|
#------------------------------------------------------------
|
||
|
|
||
|
my $q = new CGI;
|
||
|
|
||
|
if (! grep (/^state$/, $q->param))
|
||
|
{
|
||
|
showInitial ($q, '');
|
||
|
}
|
||
|
elsif ($q->param ('state') eq "perform")
|
||
|
{
|
||
|
performAndShowResult ($q);
|
||
|
}
|
||
|
elsif ($q->param ('state') eq "desktop-restore")
|
||
|
{
|
||
|
performDesktopRestore($q);
|
||
|
}
|
||
|
elsif ($q->param ('state') eq "desktop-verify")
|
||
|
{
|
||
|
performDesktopVerify($q);
|
||
|
}
|
||
|
elsif ($q->param ('state') eq "diskUsage")
|
||
|
{
|
||
|
performDiskUsage ($q);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
esmith::cgi::genStateError ($q, \%conf);
|
||
|
}
|
||
|
|
||
|
exit (0);
|
||
|
|
||
|
#------------------------------------------------------------
|
||
|
# subroutine to display initial form
|
||
|
#------------------------------------------------------------
|
||
|
|
||
|
sub showInitial ($$)
|
||
|
{
|
||
|
my ($q, $msg) = @_;
|
||
|
|
||
|
if ($msg eq '')
|
||
|
{
|
||
|
esmith::cgi::genHeaderNonCacheable ($q, \%conf,
|
||
|
'Backup or restore user data');
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
esmith::cgi::genHeaderNonCacheable
|
||
|
($q, \%conf, 'Operation status report');
|
||
|
|
||
|
print $q->p ($msg);
|
||
|
print $q->hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
print $q->p (
|
||
|
"This page allows you to create a copy of <b>your "
|
||
|
. "own</b> user data files, from your home directory "
|
||
|
. "and downloads it to your local desktop via your web "
|
||
|
. "browser. The Verify desktop backup file option can be used to\n"
|
||
|
. "view a list of files in a desktop backup file.\n");
|
||
|
|
||
|
print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=diskUsage"},
|
||
|
'Click here'),
|
||
|
'to check/refresh the disk usage for this user shown below, it may take
|
||
|
a minute the first time but should be faster afterwards.');
|
||
|
|
||
|
#show usage
|
||
|
if ( -e "/tmp/du-$acctName" )
|
||
|
{
|
||
|
my $du = '0';
|
||
|
|
||
|
open (INF,"/tmp/du-$acctName")
|
||
|
or die ("can't open du to read: $1. ");
|
||
|
|
||
|
while (<INF>)
|
||
|
{
|
||
|
($du, undef) = split(/\t/, $_);
|
||
|
}
|
||
|
close INF;
|
||
|
|
||
|
print $q->p ("When last checked with the link above your configuration "
|
||
|
. "and data files totaled <b>$du</b>. Your backup will be compressed to approx. "
|
||
|
. "half that size.\n");
|
||
|
}
|
||
|
|
||
|
my %labels = (
|
||
|
"desktop-backup" => "Backup to desktop",
|
||
|
"desktop-restore" => "Restore from desktop",
|
||
|
"desktop-verify" => "Verify desktop backup file",
|
||
|
);
|
||
|
|
||
|
my @labels = (
|
||
|
'desktop-backup',
|
||
|
'desktop-restore',
|
||
|
'desktop-verify',
|
||
|
);
|
||
|
|
||
|
my $default_action = 'desktop-backup';
|
||
|
|
||
|
print $q->startform (-method => 'POST',
|
||
|
-action => $q->url (-absolute => 1));
|
||
|
|
||
|
print $q->table ({border => 0, cellspacing => 0, cellpadding => 4});
|
||
|
|
||
|
print $q->Tr (esmith::cgi::genWidgetRow($q,"Select an action",
|
||
|
$q->popup_menu (-name => 'function',
|
||
|
-values => [ @labels ],
|
||
|
-default => $default_action,
|
||
|
-labels => \%labels)));
|
||
|
|
||
|
print "</table>\n";
|
||
|
|
||
|
print '<p>';
|
||
|
|
||
|
print $q->Tr (
|
||
|
esmith::cgi::genButtonRow(
|
||
|
$q,
|
||
|
$q->submit (-name => 'action', -value => 'Perform')
|
||
|
)
|
||
|
);
|
||
|
|
||
|
print $q->hidden (
|
||
|
-name => 'state',
|
||
|
-override => 1,
|
||
|
-default => 'perform'
|
||
|
);
|
||
|
|
||
|
print $q->endform;
|
||
|
|
||
|
# print $q->p ($q->hr, $q->font ({size => "-1"}, "www.dungog.net/sme"));
|
||
|
esmith::cgi::genFooter($q);
|
||
|
|
||
|
|
||
|
print '</FONT>';
|
||
|
print '</DIV>';
|
||
|
print $q->end_html;
|
||
|
}
|
||
|
|
||
|
sub performAndShowResult ($)
|
||
|
{
|
||
|
my ($q) = @_;
|
||
|
|
||
|
my $function = $q->param ('function');
|
||
|
|
||
|
if ($function eq 'refresh')
|
||
|
{
|
||
|
showInitial ($q, '');
|
||
|
}
|
||
|
elsif ($function eq 'desktop-backup')
|
||
|
{
|
||
|
desktopBackup();
|
||
|
}
|
||
|
elsif ($function eq 'desktop-restore')
|
||
|
{
|
||
|
desktopRestore();
|
||
|
}
|
||
|
elsif ($function eq 'desktop-verify')
|
||
|
{
|
||
|
desktopVerify();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# Unknown function - refresh the screen anyway
|
||
|
showInitial ($q, 'unknown');
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
|
||
|
esmith::cgi::genHeaderNonCacheable ($q, \%conf,
|
||
|
'X Backup or restore server data');
|
||
|
print $q->p ( $function );
|
||
|
esmith::cgi::genFooter ($q);
|
||
|
}
|
||
|
|
||
|
sub desktopBackup ()
|
||
|
{
|
||
|
|
||
|
my $directories = $q->param ('directories');
|
||
|
|
||
|
# Generate a header that will trigger a download and send data as
|
||
|
# an octet stream.
|
||
|
|
||
|
print "Expires: 0\n";
|
||
|
print "Content-type: application/octet-stream\n";
|
||
|
print "Content-disposition: inline; filename=$acctName.tgz\n";
|
||
|
print "\n";
|
||
|
|
||
|
open(RD,
|
||
|
"/bin/tar --directory / --create @directories --file=-"
|
||
|
. " | /usr/bin/gzip |");
|
||
|
|
||
|
while (<RD>)
|
||
|
{
|
||
|
print;
|
||
|
}
|
||
|
|
||
|
close RD;
|
||
|
}
|
||
|
|
||
|
sub desktopRestore ()
|
||
|
{
|
||
|
esmith::cgi::genHeaderNonCacheable ($q, \%conf,
|
||
|
'Restore server configuration');
|
||
|
|
||
|
print $q->p (
|
||
|
"This process will upload a SME Server "
|
||
|
. " backup file from your "
|
||
|
. "local desktop to your server and "
|
||
|
. "restore the configuration and user data files.\n");
|
||
|
|
||
|
print $q->p (
|
||
|
"Be very sure you are uploading the correct file, "
|
||
|
. "files with the same name will be overwritten.\n");
|
||
|
|
||
|
print $q->start_multipart_form(
|
||
|
-method => 'POST',
|
||
|
-action => $q->url (-absolute => 1));
|
||
|
|
||
|
print $q->table (
|
||
|
{border => 0, cellspacing => 0, cellpadding => 4},
|
||
|
|
||
|
esmith::cgi::genWidgetRow(
|
||
|
$q,
|
||
|
"Backup file to restore from",
|
||
|
$q->filefield(
|
||
|
-name => 'backupFile',
|
||
|
-default => "$acctName.tgz",
|
||
|
-size => 32)),
|
||
|
|
||
|
esmith::cgi::genButtonRow(
|
||
|
$q,
|
||
|
$q->submit(
|
||
|
-name => 'action',
|
||
|
-value => 'Restore')));
|
||
|
|
||
|
print $q->hidden(
|
||
|
-name => 'state',
|
||
|
-override => 1,
|
||
|
-default => 'desktop-restore');
|
||
|
|
||
|
print $q->endform;
|
||
|
|
||
|
esmith::cgi::genFooter ($q);
|
||
|
}
|
||
|
|
||
|
sub desktopVerify ()
|
||
|
{
|
||
|
esmith::cgi::genHeaderNonCacheable ($q, \%conf,
|
||
|
'Verify desktop backup file');
|
||
|
|
||
|
print $q->p (
|
||
|
"This option will display the names of all files\n"
|
||
|
. "in a previously created desktop backup file. You\n"
|
||
|
. "can use this option to verify the contents of the\n"
|
||
|
. "backup file.\n"
|
||
|
);
|
||
|
|
||
|
print $q->start_multipart_form(
|
||
|
-method => 'POST',
|
||
|
-action => $q->url (-absolute => 1)
|
||
|
);
|
||
|
|
||
|
print $q->table (
|
||
|
{border => 0, cellspacing => 0, cellpadding => 4},
|
||
|
|
||
|
esmith::cgi::genWidgetRow(
|
||
|
$q,
|
||
|
"Select backup file",
|
||
|
$q->filefield(
|
||
|
-name => 'backupFile',
|
||
|
-default => "$acctName.tgz",
|
||
|
-size => 32
|
||
|
)
|
||
|
),
|
||
|
|
||
|
esmith::cgi::genButtonRow(
|
||
|
$q,
|
||
|
$q->submit(
|
||
|
-name => 'action',
|
||
|
-value => 'Verify'
|
||
|
)
|
||
|
)
|
||
|
);
|
||
|
|
||
|
print $q->hidden(
|
||
|
-name => 'state',
|
||
|
-override => 1,
|
||
|
-default => 'desktop-verify'
|
||
|
);
|
||
|
|
||
|
print $q->endform;
|
||
|
|
||
|
esmith::cgi::genFooter ($q);
|
||
|
}
|
||
|
|
||
|
sub performDesktopRestore ($)
|
||
|
{
|
||
|
my ($q) = @_;
|
||
|
|
||
|
# Need to validate this here: $q->param ('backupFile');
|
||
|
my $lock_file = "/var/lock/subsys/e-smith-restore";
|
||
|
my $file_handle = &esmith::lockfile::LockFileOrReturn($lock_file);
|
||
|
|
||
|
unless ($file_handle)
|
||
|
{
|
||
|
esmith::cgi::genHeaderNonCacheable(
|
||
|
$q,
|
||
|
\%conf,
|
||
|
"Unable to proceed with restore of server configuration"
|
||
|
);
|
||
|
|
||
|
print $q->p (
|
||
|
$q->b (
|
||
|
"Another restore is in progress.",
|
||
|
"Please try again later."
|
||
|
)
|
||
|
);
|
||
|
|
||
|
esmith::cgi::genFooter ($q);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
# no need to reboot after restoring users data
|
||
|
#db_set_prop(\%restore, 'restore', 'state', 'running');
|
||
|
#db_set_prop(\%restore, 'restore', 'start', time);
|
||
|
#db_set_prop(\%conf, 'bootstrap-console', 'Run', 'yes');
|
||
|
|
||
|
if (open(RD, "-|"))
|
||
|
{
|
||
|
|
||
|
#----------------------------------------
|
||
|
# restore system from uploaded backup file
|
||
|
#----------------------------------------
|
||
|
|
||
|
esmith::cgi::genHeaderNonCacheable ($q, \%conf, "Restore in progress");
|
||
|
|
||
|
print $q->p ("The following files and directories have been restored:");
|
||
|
|
||
|
print "<UL>";
|
||
|
my $complete = 0;
|
||
|
while (<RD>)
|
||
|
{
|
||
|
print "<li>$_</li>\n";
|
||
|
}
|
||
|
|
||
|
print "</UL>";
|
||
|
my $message;
|
||
|
if (!close RD)
|
||
|
{
|
||
|
$message = "Restore failed! " .
|
||
|
"There was an error in reading the backup file";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$message = "Restore complete";
|
||
|
}
|
||
|
|
||
|
#db_set_prop(\%restore, 'restore', 'state', 'complete');
|
||
|
#db_set_prop(\%restore, 'restore', 'finish', time);
|
||
|
|
||
|
&esmith::lockfile::UnlockFile($file_handle);
|
||
|
|
||
|
print $q->p ($q->b ($message));
|
||
|
|
||
|
esmith::cgi::genFooter ($q);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
select(STDOUT);
|
||
|
$| = 1;
|
||
|
|
||
|
my $backupFile = $q->param ('backupFile');
|
||
|
|
||
|
my $decodeCommand = ( $backupFile =~ /\.bak$/ ) ?
|
||
|
"|/usr/bin/uudecode -o /dev/stdout" :
|
||
|
"|/usr/bin/gzip -d";
|
||
|
|
||
|
open(WR,
|
||
|
$decodeCommand
|
||
|
. " | /bin/tar --directory / --extract --verbose --file=-"
|
||
|
) || die ("Could not execute backup pipeline: $!\n");
|
||
|
|
||
|
while (<$backupFile>)
|
||
|
{
|
||
|
print WR;
|
||
|
}
|
||
|
|
||
|
close WR || die("Could not decode backup file: $!\n");
|
||
|
exit(0);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sub performDesktopVerify ($)
|
||
|
{
|
||
|
my ($q) = @_;
|
||
|
|
||
|
if (open(RD, "-|"))
|
||
|
{
|
||
|
esmith::cgi::genHeaderNonCacheable ($q,
|
||
|
\%conf, "Verify desktop backup file");
|
||
|
|
||
|
print $q->p
|
||
|
("The following files are contained in the backup file:");
|
||
|
|
||
|
print "<UL>";
|
||
|
|
||
|
my $complete = 0;
|
||
|
while (<RD>)
|
||
|
{
|
||
|
#$complete++ if /etc\/smbpasswd/;
|
||
|
print "<li>$_</li>\n";
|
||
|
}
|
||
|
|
||
|
print "</UL>";
|
||
|
my $status = close RD ?
|
||
|
($complete ?
|
||
|
"Verify complete" :
|
||
|
"End of list")
|
||
|
: "There was an error in reading the backup file";
|
||
|
print $q->p ($q->b ($status));
|
||
|
|
||
|
esmith::cgi::genFooter ($q);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
select(STDOUT);
|
||
|
$| = 1;
|
||
|
|
||
|
my $backupFile = $q->param ('backupFile');
|
||
|
|
||
|
my $decodeCommand = ( $backupFile =~ /\.bak$/ ) ?
|
||
|
"|/usr/bin/uudecode -o /dev/stdout" :
|
||
|
"|/usr/bin/gzip -d";
|
||
|
|
||
|
open(WR,
|
||
|
$decodeCommand
|
||
|
. " | /bin/tar --directory / --list --file=-"
|
||
|
) || die ("Could not decode backup file: $!\n");
|
||
|
|
||
|
while (<$backupFile>)
|
||
|
{
|
||
|
print WR;
|
||
|
}
|
||
|
|
||
|
close WR;
|
||
|
exit 0;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sub performDiskUsage ($)
|
||
|
{
|
||
|
my ($q) = @_;
|
||
|
my $acctName = $ENV{'REMOTE_USER'};
|
||
|
|
||
|
if ($acctName =~ /^([a-z][\-a-z0-9]*)$/)
|
||
|
{
|
||
|
$acctName = $1;
|
||
|
}
|
||
|
|
||
|
system ("/sbin/e-smith/signal-event", "calc-du", "$acctName") == 0
|
||
|
or die ("Error occurred while calculating disk usage.\n");
|
||
|
|
||
|
showInitial ($q, "");
|
||
|
}
|