#!/bin/perl
#============================================================= -*-perl-*-
#
# BackupPC_changePool: Move, change, or copy the pool to another
# location on the filesystem.
#
# DESCRIPTION
#
#   Usage: BackupPC_changePool --[new|switch|snapshot] <newPool>
#
#   new:      this option creates a new empty pool at <newPool> and updates
#             the global configuration file with the location.  This
#             is the default behavior.
#
#   switch:   this option updates the configuration information to look
#             at <newPool> for pool information.  This option does not
#             perform any other disk operations.
#
#   snapshot: this option places a snapshot the current pool to the
#             location specified by <newPool>.  It can later be
#             switched to with the --switch option.  The location
#             folder must be empty before the command is run.
#
# AUTHOR
#   Paul Mantz <pcmantz@zmanda.com>
#
# COPYRIGHT
#  Copyright (C) 2008 Zmanda, Inc.
#
#========================================================================
#
# Version 3.1.0, released 25 Nov 2007.
#
# See http://backuppc.sourceforge.net.
#
#========================================================================


use strict;
use warnings;
use utf8;

use Getopt::Long;
use Data::Dumper;
use File::Path;
use File::Copy;

use lib "/usr/local/BackupPC/lib";
use BackupPC::Lib;
use BackupPC::FileZIO;

#
# This should probably be integrated into BackupPC's UNIX toolchain
# location detector in the next release.
#
my $cpPath = "/bin/cp";

#
# argument handling
#
my %opts = ();
my $usage = "$0 [--new|--switch|--snapshot] <newPool>";

unless ( GetOptions( \%opts,
                     "new",
                     "switch",
                     "snapshot",
        ) ) {

    print "$usage\n";
    exit(1);
}

if ( scalar keys %opts  > 1 ) {
    print "$usage\n";
    exit(1);
}
$opts{new} = 1 if ( scalar keys %opts == 0 );

if ( scalar @ARGV != 1 ) {
    print "$usage\n";
    exit(1);
}
my $destTopDir = shift @ARGV;


#
# Invoke BackupPC libraries and constants
#
die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) );
my $TopDir = $bpc->TopDir();
my $BinDir = $bpc->BinDir();
my %Conf   = $bpc->Conf();


#
# Die if the server is running
#
my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}, 1);
if ( $err eq "" ) {
    print <<EOF;

BackupPC is running on $Conf{ServerHost}.  You need to stop BackupPC
before you can move the pool.  Depending upon your installation, you
could run "/etc/init.d/backuppc stop".

EOF
    exit(1);
}


#
# Perform the action
#
if (  $opts{new} ) {
    makeDataDir($destTopDir);
    setNewPool($bpc, $destTopDir);

} elsif ( $opts{switch} ) {
    setNewPool($bpc, $destTopDir);

} elsif ( $opts{snapshot} ) {
    die "This option is not supported yet.  Please perform this action manually.\n";
    copyDataDir($TopDir, $destTopDir);

} else {
    exit(1);
}


##############################################################################
# Subroutines
##############################################################################

sub copyDataDir
{
    my ( $srcDir, $dstDir ) = @_;
    my ( $login, $pass, $uid, $gid ) = getpwnam $Conf{BackupPCUser};

    if ( -e $dstDir and !-d $dstDir) {
        die "$dstDir is not a directory.  Please choose another directory."
    }

    mkpath($dstDir,0, 755) unless ( -d $dstDir);
    unless ( -d $dstDir and chown( $uid, $gid, $dstDir ) ) {
        die "can't to create or chown $dstDir\n";
    }

    rcopy($srcDir, $dstDir);
}


sub moveDataDir
{
    my ( $srcDir, $dstDir ) = @_;
    my ( $login, $pass, $uid, $gid ) = getpwnam $Conf{BackupPCUser};

    foreach my $dir (  qw/pool cpool pc trash/ ) {

        #print STDERR "move(\"$srcDir/$dir\", \"$dstDir/$dir\")\n"; next;
        move("$srcDir/$dir", "$dstDir/$dir")
            or die("can't move $srcDir/$dir to $dstDir/$dir\n");
    }
}


sub makeDataDir
{
    my ($dataDir) = @_;
    #print STDERR "\$dataDir=$dataDir\n";

    my ($login, $pass, $uid, $gid) = getpwnam $Conf{BackupPCUser};

    foreach my $dir ( "$dataDir",       "$dataDir/pool",
                      "$dataDir/cpool", "$dataDir/pc",
                      "$dataDir/trash", ) {

        #print STDERR "mkpath( $dir, 0, 750 )\n"; next;
        mkpath( $dir, 0, 750 ) unless ( -d $dir );
        unless ( -d $dir and chown( $uid, $gid, $dir ) ) {
            die "failed to create or chown $dir\n";
        }
    }
}


sub setNewPool
{
    my ($bpc, $destTopDir) = @_;
    my %Conf  = $bpc->Conf();

    #print STDERR "\$bpc->ConfigDataWrite( undef, \%Conf )\n"; return;
    $Conf{TopDir} = $destTopDir;
    $bpc->ConfigDataWrite( undef, \%Conf );
}


#
# This function should be replaced by a perl function instead of a
# system call in the Future.  However, File::Copy::Recursive is not in
# the standard perl distribution.
#
sub rcopy
{
    my ($src, $dst) = @_;
    my @command = ($cpPath, "-Rd", $src, $dst);

    undef $?;
    system @command;

    if ( !defined $? ) {
        die join(' ', @command)." failed to execute:$!\n";

    } elsif  ($? == -1) {
        die "cp failed to execute: $!\n";

    } elsif  ($? & 127) {
        die sprintf  "cp died with signal %d, %s coredump\n",
            ($? & 127),  ($? & 128) ? 'with' : 'without';

    } else {
        printf "cp exited with value %d\n", $? >> 8;
    }
}
