#!/usr/bin/perl -w
=pod
//////////////////////////////////////////////////////////////////////////////
//
// Name: diaser
// Date: 21/01/2011
//
// Author: Damian Lajos Brasher
// WWW: www.diaser.org.uk http://sourceforge.net/projects/diaser
// Email: dlb@interlinux.org.uk
// License GPL Version 3, 29 June 2007 gpl.txt
// First created 13th-15th October 2008 - Nick: 'Moonlight Sonata'.
//
//////////////////////////////////////////////////////////////////////////////

Based on http://tools.ietf.org/html/draft-brasher-ltasp-04
in turn on the diap bash prototype pre-aplha-final-0.3.4.

This script is the installer and management system for the DIASER system.
=cut

use strict;
use warnings;
use English;
require 5.008_008;

# ensure these modules are loaded at run time
use AppConfig qw(:argcount);
use Carp;
use Data::Password qw(:all);
use Getopt::Long;
use Net::SFTP;
use Net::SSH::Perl; 
use Time::HiRes;
use Term::ReadKey;
#use Term::ProgressBar - maybe use this module later

#  the only global variables
my $name = "DIASER";
my $version = "1.1.0 beta-3-dev";
my $user_pass;  
my $sudo_pass_a;
my $sudo_pass_b;
my $sudo_pass_c;

# flush all output in case a file has not been closed
# how much does this help? investigate further
if (@ARGV) {
    local $| = 1; # ouput autoflush
}

# configuration file 
my $config = AppConfig->new();

# configuration variable definition table

# the order of these is important, this order is used when passing
# parameters to subroutines and when they are unpacked 
$config->define(
    'NUM_YEARS'     => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 3  },
    'START_YEAR'    => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 2009 },
    'HOUR1'         => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0 },
    'HOUR2'         => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 3 },
    'NODE_A'        => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0.0.0.0 },
    'NODE_B'        => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0.0.0.0 },
    'NODE_C'        => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0.0.0.0 },
    'PORT_A'        => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 22 },
    'PORT_B'        => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 22 },
    'PORT_C'        => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 22 },
    'DRY_RUN'       => { ARGCOUNT => ARGCOUNT_NONE,DEFAULT => 0 },
    'LOW_MAX_BW'    => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 125000 },
    
    # time zone vars will use a tzone lookup table sub 
    'TZONE_A'       => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0 },
    'TZONE_B'       => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0 },
    'TZONE_C'       => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0 },
    
    'USER_ACC'      => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "diasertest" },
    'TOUT'          => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 10800 },
    'DIR_A'         => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "/home/" },
    'DIR_B'         => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "/home/" },
    'DIR_C'         => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "/home/" },

    # additional variables to cope with the Ubuntu (and other distros) forced
    # use of sudo; if the USE_SUDO variable is positive then commands that
    # require root access will use sudo with the -S switch, 
    # i.e. echo "root_password" | sudo -S command
    # also the username of the account to connect to - instead of root - 
    # will be used: SUDO_ACCOUNT_NAME_{node}
    'USE_SUDO'      => { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => "0" },
    'SUDO_ACCOUNT_NAME_A' => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "usera" },
    'SUDO_ACCOUNT_NAME_B' => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "userb" },
    'SUDO_ACCOUNT_NAME_C' => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "userc" },

    # user defined variables for the DIASER filling mechanism / fill_diaser.pl
    'FILL_START_TIME'   => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "10" },
    'VOLUME_DIR'    => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "/mnt/bkp/" },
    'DIFF_CONST_PREFIX' => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "diff" },
    'COLLECT_FULL'  => { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => "1" },
    'COLLECT_FULL_DAY'  => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "2" },
    'FULL_PREFIX'   => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "full" },
    
    # variables for archive retrieve
    'R_YEAR'    => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "2009" },
    'R_MONTH'   => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "7" },
    'R_DAY'     => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "21" },
    'R_FULL'    => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "Full01" },
    
    # configuration file
    'CONF'    => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => "diaser.conf" },
    
    # development only
    'NUM_MACHINES'  => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 3 },
    'NUM_MONTHS'    => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 12 },
    'START_DOM'     => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 4 },
    'START_MONTH'   => { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 27 },
);

# process two configuration files, I use one for development
# only one needs to be read and that's the one you use, diaser.conf
# only diaser.conf is used by default

my $forced = $ARGV[0];
if (-f $forced) {
    $config->file($forced);
    $ARGV[0] = undef;
} else {
    $config->file("diaser.conf");
}    

# prints usage if no command line parameters are passed or there 
# is an unknown parameter or help, in alphabetic order
my $configure;
my $bandwidth;
my $extend;
my $install;
my $list;
my $lock;
my $logs;
my $migrate;
my $modify;
    
    # additional *uninitialised* configure variables / options, 
    # only the option modify will read and process these
    my $numyears;
    my $startyear;
    my $phase1;
    my $phase2;
    my $nodea;
    my $nodeb;
    my $nodec;
    my $porta;
    my $portb;
    my $portc;
    my $dryrun;
    my $lmb;
    my $tzone_a;
    my $tzone_b;
    my $tzone_c;
    my $useracc;
    my $tout;
    my $userdira;
    my $userdirb;
    my $userdirc;
    my $usesudo;
    my $sudoaccnamea;
    my $sudoaccnameb;
    my $sudoaccnamec;
    my $fillstarttime;
    my $volumedir;
    my $diffconstprefix;
    my $collectfull;
    my $collectfullday;
    my $fullprefix;

my $recreate;
my $pause;
my $remove;
my $resume;
my $retrieve;

    # additional retrieve subroutine variables
    my $r_year;
    my $r_month;
    my $r_day;
    my $r_full;

my $stats;
my $stop;
my $upgrade;
my $ver;
my $help;

GetOptions( 
    'config|configure'  => \$configure, 
    'bandwidth'         => \$bandwidth,
    'extend'            => \$extend,
    'install'           => \$install,
    'list'              => \$list,
    'lock'              => \$lock,
    'logs'              => \$logs,
    'migrate'           => \$migrate,
    'modify'            => \$modify,
        
        'numyears=i'=> \$numyears,
        'startyear=i'=> \$startyear,
        'phase1=i'=> \$phase1,
        'phase2=i'=> \$phase2,
        'nodea=s'=> \$nodea,
        'nodeb=s'=> \$nodeb,
        'nodec=s'=> \$nodec,
        'porta=i'=> \$porta,
        'portb=i'=> \$portb,
        'portc=i'=> \$portc,
        'dryrun=i'=> \$dryrun,
        'lmb=i'=> \$lmb,
        'tzone_a=i'=> \$tzone_a,
        'tzone_b=i'=> \$tzone_b,
        'tzone_c=i'=> \$tzone_c,
        'useracc=s'=> \$useracc,
        'tout=i'=> \$tout,
        'userdira=s'=> \$userdira,
        'userdirb=s'=> \$userdirb,
        'userdirc=s'=> \$userdirc,
        'usesudo=i' => \$usesudo,
        'sudoaccnamea=s' => \$sudoaccnamea,
        'sudoaccnameb=s' => \$sudoaccnameb,
        'sudoaccnamec=s' => \$sudoaccnamec,
        'fillstarttime=i' => \$fillstarttime,
        'volumedir=s' => \$volumedir,
        'diffconstprefix=s' => \$diffconstprefix,
        'collectfull=i' => \$collectfull,
        'collectfullday=i' => \$collectfullday,
        'fullprefix=s'=> \$fullprefix,
    
    'pause'             => \$pause,
    'recreate'          => \$recreate,
    'remove'            => \$remove,
    'resume'            => \$resume,
    # do I need to add the additional retrieve options as for modify above?
    'retrieve'          => \$retrieve,

        'year'=> \$r_year,
        'month'=> \$r_month,
        'day'=> \$r_day,
        'full'=> \$r_full,

    'stats'             => \$stats,
    'stop'              => \$stop,
    'upgrade'           => \$upgrade,
    'version'           => \$ver,
    'help|?'            => \$help,

)or usage();

# configure 
if ($configure) { 
    
    # query driven configure tool for new and existing diaser deployments
    configure($config->NUM_YEARS, $config->START_YEAR, 
    $config->HOUR1, $config->HOUR2, 
    $config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, 
    $config->DRY_RUN, $config->LOW_MAX_BW, 
    $config->TZONE_A, $config->TZONE_B, $config->TZONE_C, 
    $config->USER_ACC, $config->TOUT, 
    $config->DIR_A, $config->DIR_B, $config->DIR_C, 
    $config->USE_SUDO,
    $config->SUDO_ACCOUNT_NAME_A,
    $config->SUDO_ACCOUNT_NAME_B,
    $config->SUDO_ACCOUNT_NAME_C,
    $config->FILL_START_TIME, $config->VOLUME_DIR, 
    $config->DIFF_CONST_PREFIX, $config->COLLECT_FULL, 
    $config->COLLECT_FULL_DAY, $config->FULL_PREFIX, 
    $config->NUM_MACHINES, $config->NUM_MONTHS, $config->START_MONTH, 
    $config->START_DOM);  
}

if ($bandwidth) {
    
    # calculate real bandwidth throughput between nodeX-Y
    bandwidth($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C,
    $config->USER_ACC);
}

if ($extend) {
    # extend maximum storage structure date 
    extend($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
}

# install DIASER
elsif ($install) {
    print <<EOT;
 
    Welcome to ${name}_${version}.

    Install will login and setup user accounts on previously selected 
    machines, after installation bandwidth to and from machines will be
    used, it is recommended you read supplied documentation or the diaser man page

    This software is distributed under GPL Version 3, 29 June 2007. 
 
    Continue?

EOT
    # select yes to continue or no to exit
    my $response = "";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response;
        if ($response =~ /n|no/ix) { exit; }
    }
 
    print "Please wait, this may take a few minutes...\n";
 
    # reset permissions of files to be transferred 
    chmod 0770, "tab_a.pl", "tab_b.pl", "tab_c.pl";

    # call subroutines

    # calculate_lmb(); # just returns user set value - full function tbd 
    # adjust_tzone();  # same as above, set in diaser.conf
 
    create_accounts($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC,
    $config->DIR_A, $config->DIR_B, $config->DIR_C, 
    $config->USE_SUDO, $config->SUDO_ACCOUNT_NAME_A, 
    $config->SUDO_ACCOUNT_NAME_B, $config->SUDO_ACCOUNT_NAME_C);
 
    gen_dirs($config->NUM_YEARS, $config->START_YEAR, $config->NODE_A, 
    $config->NODE_B, $config->NODE_C, $config->PORT_A, $config->PORT_B, 
    $config->PORT_C, $config->USER_ACC, $config->DIR_A, $config->DIR_B, 
    $config->DIR_C);
 
    passwordless_login($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
 
    gen_hvautoc($config->NUM_YEARS, $config->START_YEAR, $config->HOUR1, 
    $config->HOUR2, $config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C,
    $config->DRY_RUN, $config->LOW_MAX_BW, 
    $config->TZONE_A, $config->TZONE_B, $config->TZONE_C, 
    $config->USER_ACC, $config->TOUT,
    $config->NUM_MONTHS, $config->START_MONTH, $config->START_DOM,
    $config->COLLECT_FULL_DAY);
 
    send_hvautoc_algol($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
 
    gen_fill($config->TZONE_A,
    $config->USER_ACC, $config->DIR_A, $config->FILL_START_TIME, 
    $config->VOLUME_DIR, $config->DIFF_CONST_PREFIX, $config->COLLECT_FULL, 
    $config->COLLECT_FULL_DAY, $config->FULL_PREFIX);
    
    send_fill($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
    
    send_cron_defs($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
 
    clean();

    print "\n";
    print "IMPORTANT, One time only - you will need to login from the" 
         ." diaser account\n";
    print "on each node to accept the certificates between nodes, like" 
         ." the 1st time you SSH into a box.\n";
    print "\n";
    print "A->B, A->C, B->A, B->C, C->A and C->B\n";
    print "\n";
    print "Afterwards logins between nodes are password-less, this step" 
         ." will allow DIASER to begin work.\n";
    print "\n";
    print "This step may be automated depending on user feedback.\n";
    print "\n";
    exit;
}

# list volumes stored in system
elsif ($list) { 
    list($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC, 
    $config->DIR_A, $config->DIR_B, $config->DIR_C,
    $config->DIFF_CONST_PREFIX, $config->FULL_PREFIX); 
}

# lock DIASER accounts
elsif ($lock) {
    lockout($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC,
    $config->USE_SUDO, $config->SUDO_ACCOUNT_NAME_A, 
    $config->SUDO_ACCOUNT_NAME_B, $config->SUDO_ACCOUNT_NAME_C);
}

# condensed log readings from each node
elsif ($logs) { 
    logs($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC, 
    $config->DIR_A, $config->DIR_B, $config->DIR_C,
    $config->DIFF_CONST_PREFIX, $config->FULL_PREFIX); 
}

# migrate node to another location
elsif ($migrate) {
    migrate($config->NODE_A, $config->NODE_B, $config->NODE_C, 
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
}

# modify
elsif ($modify) { 

    # validate then update modified values if any
    if ($numyears)  { 
        if (is_integer($numyears)) {
            $config->NUM_YEARS($numyears); 
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($startyear) { 
        if (is_integer($startyear)) {
    $config->START_YEAR($startyear);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($phase1)    { 
        if (is_integer($phase1) and ($phase1 < 12)) {
    $config->HOUR1($phase1); 
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
         
    if ($phase2)    { 
        if (is_integer($phase2) and ($phase2 < 12)) {
    $config->HOUR2($phase2);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($nodea)     { 
        if (is_ip($nodea)) {
    $config->NODE_A($nodea);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($nodeb)     { 
        if (is_ip($nodeb)) {
    $config->NODE_B($nodeb);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($nodec)     { 
        if (is_ip($nodec)) {
    $config->NODE_C($nodec);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($porta)     { 
        if (is_integer($porta)) {
    $config->PORT_A($porta);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($portb)     { 
        if (is_integer($portb)) {
    $config->PORT_B($portb);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($portc)     { 
        if (is_integer($portc)) {
    $config->PORT_C($portc);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($dryrun)    { # not integer greater that 1, just check is string 
        if (is_string($dryrun)) {
    $config->DRY_RUN($dryrun);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($lmb)       { 
        if (is_integer($lmb)) {
    $config->LOW_MAX_BW($lmb);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($tzone_a)     { 
        if (is_integer_plus_minus($tzone_a)) {
    $config->TZONE_A($tzone_a);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($tzone_b)     { 
        if (is_integer_plus_minus($tzone_b)) {
    $config->TZONE_B($tzone_b);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($tzone_c)     { 
        if (is_integer_plus_minus($tzone_c)) {
    $config->TZONE_C($tzone_c);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($useracc)   { 
        if (is_string($useracc)) {
    $config->USER_ACC($useracc); 
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    }    
    
    if ($tout)      { 
        if (is_integer($tout)) {
    $config->TOUT($tout); 
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($userdira)  { 
        if (is_directory($userdira)) {
    $config->DIR_A($userdira);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($userdirb)  { 
        if (is_directory($userdirb)) {
    $config->DIR_B($userdirb);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($userdirc)  { 
        if (is_directory($userdirc)) {
    $config->DIR_C($userdirc);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
   
    if ($usesudo)  { 
        if (is_integer($usesudo)) {
    $config->USE_SUDO($usesudo);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($sudoaccnamea)  { 
        if (is_string($sudoaccnamea)) {
    $config->SUDO_ACCOUNT_NAME_A($sudoaccnamea);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($sudoaccnameb)  { 
        if (is_string($sudoaccnameb)) {
    $config->SUDO_ACCOUNT_NAME_B($sudoaccnameb);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($sudoaccnamec)  { 
        if (is_string($sudoaccnamec)) {
    $config->SUDO_ACCOUNT_NAME_C($sudoaccnamec);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($fillstarttime)  { 
        if (is_integer_inc_zero($fillstarttime)) {
    $config->FILL_START_TIME($fillstarttime);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    if ($volumedir)  { 
        if (is_directory($volumedir)) {
    $config->VOLUME_DIR($volumedir);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    if ($diffconstprefix)  { 
        if (is_string($diffconstprefix)) {
    $config->DIFF_CONST_PREFIX($diffconstprefix);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    if ($collectfull)  { 
        if (is_integer($collectfull)) {
    $config->COLLECT_FULL($collectfull);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($collectfullday)  { 
        if (is_integer($collectfullday)) {
    $config->COLLECT_FULL_DAY($collectfullday);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    if ($fullprefix)  { 
        if (is_string($fullprefix)) {
    $config->FULL_PREFIX($fullprefix);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    # write the new values to the default configuration file diaser.conf
    write_to_config($config->NUM_YEARS, $config->START_YEAR, 
    $config->HOUR1, $config->HOUR2, 
    $config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, 
    $config->DRY_RUN, $config->LOW_MAX_BW, 
    $config->TZONE_A, $config->TZONE_B, $config->TZONE_C, 
    $config->USER_ACC, $config->TOUT, 
    $config->DIR_A, $config->DIR_B, $config->DIR_C, 
    $config->USE_SUDO,
    $config->SUDO_ACCOUNT_NAME_A,
    $config->SUDO_ACCOUNT_NAME_B,
    $config->SUDO_ACCOUNT_NAME_C,
    $config->FILL_START_TIME, $config->VOLUME_DIR, 
    $config->DIFF_CONST_PREFIX, $config->COLLECT_FULL, 
    $config->COLLECT_FULL_DAY, $config->FULL_PREFIX, 
    $config->NUM_MACHINES, $config->NUM_MONTHS, $config->START_MONTH, 
    $config->START_DOM);

    # modify settings on the nodes
    modify();
    clean();
}

# pause 
elsif ($pause) { 
    pause($config->NODE_A, $config->NODE_B, $config->NODE_C, 
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
}

# recreate node from scratch
elsif ($recreate) {
    recreate($config->NUM_YEARS, $config->START_YEAR,
    $config->NODE_A, $config->NODE_B, $config->NODE_C, 
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC,
    $config->DIR_A, $config->DIR_B, $config->DIR_C);
}

# remove DIASER
elsif ($remove) {
 print <<EOT;
        
    ${name}_${version}.

    WARNING - You will remove *ALL* stored data from nodes, it is 
    recommended you read supplied documentation or the website 
    http://www.diaser.org.uk/docs.html 

    This software is distributed under GPL Version 3, 29 June 2007.
    See gpl.txt in this directory.
        
    Continue with this DANGEROUS operation?

EOT
 
    # select yes to continue or no to exit
    my $response = "";
    while ($response !~ /yes|y|no|n/ix) {
    print "Please enter (y)yes or (n)no\n";
    $response = <>;
    chomp $response; 
    if ($response =~ /n|no/ix) { exit; }
    }  
    print "This will take a couple of minutes...\n";
    print "\n";
 
    remove ($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC,
    $config->USE_SUDO, $config->SUDO_ACCOUNT_NAME_A, 
    $config->SUDO_ACCOUNT_NAME_B, $config->SUDO_ACCOUNT_NAME_C);
 
    exit;
}

# resume
elsif ($resume) { 
    resume($config->NODE_A, $config->NODE_B, $config->NODE_C, 
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
}

# retrieve data
elsif ($retrieve) {

if ($nodea)     { 
        if (is_ip($nodea)) {
    $config->NODE_A($nodea);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($nodeb)     { 
        if (is_ip($nodeb)) {
    $config->NODE_B($nodeb);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($nodec)     { 
        if (is_ip($nodec)) {
    $config->NODE_C($nodec);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($porta)     { 
        if (is_integer($porta)) {
    $config->PORT_A($porta);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($portb)     { 
        if (is_integer($portb)) {
    $config->PORT_B($portb);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($portc)     { 
        if (is_integer($portc)) {
    $config->PORT_C($portc);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    if ($useracc)   { 
        if (is_string($useracc)) {
    $config->USER_ACC($useracc); 
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 
    
    # retrieve specific command line options
    if ($r_year)     { 
        if (is_string($r_year)) {
    $config->R_YEAR($r_year);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    if ($r_month)     { 
        if (is_string($r_month)) {
    $config->R_MONTH($r_month);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    if ($r_day)     { 
        if (is_string($r_day)) {
    $config->R_DAY($r_day);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    if ($r_full)     { 
        if (is_string($r_full)) {
    $config->R_FULL($r_full);  
        } else { 
            print "incorrect input format\n";
            exit; 
          }
    } 

    retrieve($config->NODE_A, $config->NODE_B, $config->NODE_C,
            $config->PORT_A, $config->PORT_B, $config->PORT_C, 
            $config->USER_ACC,
            $config->R_YEAR, $config->R_MONTH, $config->R_DAY, 
            $config->R_FULL);
}

# stats, tracking and information
elsif ($stats) { 
 
    stats($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC, 
    $config->DIR_A, $config->DIR_B, $config->DIR_C,
    $config->DIFF_CONST_PREFIX, $config->FULL_PREFIX); 
}

# stop
elsif ($stop) { 
    stop($config->NODE_A, $config->NODE_B, $config->NODE_C, 
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
}

# upgrade 
elsif ($upgrade) { 
    upgrade(); 
    clean();
}

# version 
elsif ($ver) { 
    print "\n";
    print "DIASER $version\n\n";
    system "perl -v";
}

# help menu
elsif ($help) { usage(); }

# catch all
else { usage(); }

###############################################################################
# subroutines - listed in alphabetical order except 
# input validation and tests under their own title at the bottom of the script
#
##############################################################################

# add more years to an existing install
sub add_years { 
 
    print "add_years not yet implemented\n";
    exit;
}

# ask for diaser account password on the fly and don't store on disk
# generate an encrypted version. If this is a new password then run
# a quality check using the module Data::Password
sub ask_diaser_pass { 

# unpack scalar parameters, new is either 1 or 0, 0 to use an existing
# password or 1 may be called to change a diaser password
my ($new) = @_;
   
    my $diaser_pass1 = "1";
    my $diaser_pass2 = "2";
    my $crypt_user_pass;
    while ($diaser_pass1 ne $diaser_pass2) {
    print "Please enter a password for all the diaser accounts\n";
    ReadMode('noecho'); # no echo to display when entering pass
    $diaser_pass1 = <>;
    chomp $diaser_pass1;
        
        # test for password strength using module Data::Password
        if ($new == 1) {
            $MAXLEN = 12;
            $MINLEN = 6;
            $DICTIONARY = 0;  
            $GROUPS = 2;
            $FOLLOWING = 2;
            
            # warn the user about a possible bad password choice
            my $IsBad = IsBadPassword("$diaser_pass1");
            if ($IsBad) {
                print "Warning only: ";
                print "$IsBad";
                print "\n";
            }
        }
        
        # if not a new diaser password then continue here
        print "Please enter the password again\n";
        $diaser_pass2 = <>;
        chomp $diaser_pass2;
        my $salt = substr($diaser_pass2,0,2); # get first 2 chars for salt
        $crypt_user_pass = crypt($diaser_pass2, $salt); # encrypt pass
        ReadMode('normal'); # display echo back on
            if ($diaser_pass1 ne $diaser_pass2) { 
            print "The passwords are not the same, try again\n";
            }
    }
    print "Thank you\n";
    print "\n";
    my $plain_user_pass = $diaser_pass2; 
    
    # plain-text user pass and encrypted user pass returned
    my @diaser_passwords = ($plain_user_pass, $crypt_user_pass); 
    return @diaser_passwords;
}

# ask for node root account passwords on the fly and don't store on disk
sub ask_node_pass { 
    
    # node A
    my $node_a_pass1 = "1"; 
    my $node_a_pass2 = "2";
    while ($node_a_pass1 ne $node_a_pass2) {
    print "Please enter the root password for node A\n";
    ReadMode('noecho');
    $node_a_pass1 = <>;
    chomp $node_a_pass1;
    print "Please enter the password again\n";
    $node_a_pass2 = <>;
    chomp $node_a_pass2;
    ReadMode('normal');
        if ($node_a_pass1 ne $node_a_pass2) { 
        print "The passwords are not the same, try again\n";
        } 
    }
    my $root_pass_node_a = $node_a_pass2; 
    print "Thank you\n";
    print "\n";

    # node B 
    my $node_b_pass1 = "1";
    my $node_b_pass2 = "2";
    while ($node_b_pass1 ne $node_b_pass2) {
    print "Please enter the root password for node B\n";
    ReadMode('noecho');
    $node_b_pass1 = <>;
    chomp $node_b_pass1; 
    print "Please enter the password again\n";
    $node_b_pass2 = <>;
    chomp $node_b_pass2; 
    ReadMode('normal');
        if ($node_b_pass1 ne $node_b_pass2) { 
        print "The passwords are not the same, try again\n";
        }  
    }
    my $root_pass_node_b = $node_b_pass2; 
    print "Thank you\n";
    print "\n";

    # node C
     my $node_c_pass1 = "1";
    my $node_c_pass2 = "2";
    while ($node_c_pass1 ne $node_c_pass2) {
    print "Please enter the root password for node C\n";
    ReadMode('noecho');
    $node_c_pass1 = <>;
    chomp $node_c_pass1; 
    print "Please enter the password again\n";
    $node_c_pass2 = <>;
    chomp $node_c_pass2; 
    ReadMode('normal');
        if ($node_c_pass1 ne $node_c_pass2) { 
        print "The passwords are not the same, try again\n";
        } 
    }
    my $root_pass_node_c = $node_c_pass2; 
    print "Thank you\n";
    print "\n";
    
    # return an array of new root passwords
    my @root_passwords = ($root_pass_node_a,$root_pass_node_b,
    $root_pass_node_c);
    return @root_passwords;
}

# ask for sudo account password on the fly and don't store on disk
# generate an encrypted version. If this is a new password then run
# a quality check using the module Data::Password
sub ask_sudo_pass { 

# unpack scalar parameters, new is either 1 or 0, 0 to use an existing
# password or 1 may be called to change a sudo user's password
    my ($new) = @_;
 
    # Node A sudo SSH account
    my $sudo_pass1_a = "1";
    my $sudo_pass2_a = "2";
    my $crypt_sudo_pass_a;
    while ($sudo_pass1_a ne $sudo_pass2_a) {
    print "Please enter a password for node A SSH account\n";
    ReadMode('noecho'); # no echo to display when entering pass
    $sudo_pass1_a = <>;
    chomp $sudo_pass1_a; 
        
        # test for password strength using module Data::Password
        if ($new == 1) {
            $MAXLEN = 12;
            $MINLEN = 6;
            $DICTIONARY = 0;  
            $GROUPS = 2;
            $FOLLOWING = 2;
            
            # warn the user about a possible bad password choice
            my $IsBad = IsBadPassword("$sudo_pass1_a");
            if ($IsBad) {
                print "Warning only: ";
                print "$IsBad";
                print "\n";
            }
        } 
        
    # if not a new sudo password then continue here
    print "Please enter the password again\n";
    $sudo_pass2_a = <>;
    chomp $sudo_pass2_a; 
    my $salt = substr($sudo_pass2_a,0,2); # get first 2 chars for salt
    $crypt_sudo_pass_a = crypt($sudo_pass2_a, $salt); # encrypt pass
    ReadMode('normal'); # display echo back on
        if ($sudo_pass1_a ne $sudo_pass2_a) { 
            print "The passwords are not the same, try again\n";
        } 
    }
    
    # Node B sudo SSH account
    my $sudo_pass1_b = "1";
    my $sudo_pass2_b = "2";
    my $crypt_sudo_pass_b;
    while ($sudo_pass1_b ne $sudo_pass2_b) {
    print "Please enter a password for node B SSH account\n";
    ReadMode('noecho'); # no echo to display when entering pass
    $sudo_pass1_b = <>;
    chomp $sudo_pass1_b; 
        
        # test for password strength using module Data::Password
        if ($new == 1) {
            $MAXLEN = 12;
            $MINLEN = 6;
            $DICTIONARY = 0;  
            $GROUPS = 2;
            $FOLLOWING = 2;
            
            # warn the user about a possible bad password choice
            my $IsBad = IsBadPassword("$sudo_pass1_b");
            if ($IsBad) {
                print "Warning only: ";
                print "$IsBad";
                print "\n";
            }
        } 
        
        # if not a new sudo password then continue here
        print "Please enter the password again\n";
    $sudo_pass2_b = <>;
    chomp $sudo_pass2_b; 
    my $salt = substr($sudo_pass2_b,0,2); # get first 2 chars for salt
    $crypt_sudo_pass_b = crypt($sudo_pass2_b, $salt); # encrypt pass
    ReadMode('normal'); # display echo back on
        if ($sudo_pass1_b ne $sudo_pass2_b) { 
            print "The passwords are not the same, try again\n";
        } 
 }
    
    # Node C sudo SSH account
    my $sudo_pass1_c = "1";
    my $sudo_pass2_c = "2";
    my $crypt_sudo_pass_c;
    while ($sudo_pass1_c ne $sudo_pass2_c) {
    print "Please enter a password for node C SSH account\n";
    ReadMode('noecho'); # no echo to display when entering pass
    $sudo_pass1_c = <>;
    chomp $sudo_pass1_c; 
        
        # test for password strength using module Data::Password
        if ($new == 1) {
            $MAXLEN = 12;
            $MINLEN = 6;
            $DICTIONARY = 0;  
            $GROUPS = 2;
            $FOLLOWING = 2;
            
            # warn the user about a possible bad password choice
            my $IsBad = IsBadPassword("$sudo_pass1_c");
            if ($IsBad) {
                print "Warning only: ";
                print "$IsBad";
                print "\n";
            }
        } 
        
    # if not a new sudo password then continue here
    print "Please enter the password again\n";
    $sudo_pass2_c = <>;
    chomp $sudo_pass2_c; 
    my $salt = substr($sudo_pass2_c,0,2); # get first 2 chars for salt
    $crypt_sudo_pass_c = crypt($sudo_pass2_c, $salt); # encrypt pass
    ReadMode('normal'); # display echo back on
        if ($sudo_pass1_c ne $sudo_pass2_c) { 
            print "The passwords are not the same, try again\n";
        } 
 }
   
    print "Thank you\n";
    print "\n";
    my $plain_sudo_pass_a = $sudo_pass2_a; 
    my $plain_sudo_pass_b = $sudo_pass2_b; 
    my $plain_sudo_pass_c = $sudo_pass2_c; 
    
    # plain-text user pass and encrypted user pass returned
    my @sudo_passwords = 
    ($plain_sudo_pass_a, $crypt_sudo_pass_a, 
    $plain_sudo_pass_b, $crypt_sudo_pass_b, 
    $plain_sudo_pass_c, $crypt_sudo_pass_c); 
    
    return @sudo_passwords;
} 

# Calculate and display the real throughput between chosen nodes X and Y
sub bandwidth {   
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $porta, $portb, $portc, $user_acc) = @_;
 
    print <<EOT;

    This option will allow you to view the real, not theoretical, data 
    throughput between two of your chosen storage nodes.

    You will need to have the Open-Source tool, gnuplot, installed on the 
    system from which you are running this application.
    
    This tool will attempt to download and compile the binary NPtcp from the
    NetPIPE utility suite: http://bitspjoule.org/netpipe/. The tool operates 
    over port 5002 and stats will be collected from the sender.

    Proceed?

EOT
 
    # select yes to continue or no to exit
    my $response = "";
    while ($response !~ /yes|y|no|n/ix) {
    print "Please enter (y)yes or (n)no\n";
    $response = <>;
    chomp $response; 
    if ($response =~ /n|no/ix) { exit; }
 }

=pod
basic procedure
1) request 2 x nodes for the test
2) request diaser account password
3) generate run_listener.pl and run_sender.pl
4) cp the NPtcp binary over to the nodes
5) cp run_listener.pl and run_sender.pl over to the nodes
6) run run_listener.pl and run_sender.pl
7) wait 60 seconds, progress bar
8) collect np.out from the listener
9) check if gnuplot is installed, if not exit cleanly
10) write gnuplot script to file
11) run gnuplot and exit
=cut

    my $test = 0;
    my $nl_choice;  # node listener
    my $ns_choice;  # node sender
    my $l_node;     # listener node
    my $s_node;     # sender node
    my $l_port;     # listener port
    my $s_port;     # sender port
   
    # download and compile NPtcp
    my $nptcp_dir = "NetPIPE-3.7.1";
    
    # has download occurred before?
    my $output = `ls`;

    if ($output =~ /NetPIPE.*tar.gz/x) {
        print "NetPIPE already downloaded\n";
    } else {
        # download NetPIPE
        system "wget http://bitspjoule.org/netpipe/code/NetPIPE-3.7.1.tar.gz";
        # untar NetPIPE
        system "tar -xzvf NetPIPE-3.7.1.tar.gz";
    }
    
    # build NPtcp
    chdir $nptcp_dir or die "Can't change to specified directory: $!";
    system "make";
    chdir "../" or die "Can't change to specified directory: $!";
    
    # which listener node
    while ($test == 0) {
        print "Which node will be the listener? [A, B or C]\n";
  # read input
        $nl_choice = <>;
        chomp $nl_choice; 
  # test if string and A, B or C
        if ($nl_choice =~ /A|B|C/x) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter A, B or C:\n\n";
  }
    }
    
    # assign listener node
    if ($nl_choice eq "A") { 
        $l_node = $node_a;
        $l_port = $porta;
    }
    if ($nl_choice eq "B") { 
        $l_node = $node_b;
        $l_port = $portb;
    }
    if ($nl_choice eq "C") { 
        $l_node = $node_c;
        $l_port = $portc;
    }

    # reset test
    $test = 0;
    
    # which sender node
    while ($test == 0) {
        print "Which node will be the receiver? [A, B or C]\n";
  # read input
        $ns_choice = <>;
        chomp $ns_choice; 
  # test if string and A, B or C
        if ($ns_choice =~ /A|B|C/x) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter A, B or C:\n\n";
  }
    }
    
    # assign source node
    if ($ns_choice eq "A") { 
        $s_node = $node_a;
        $s_port = $porta;
    }
    if ($ns_choice eq "B") { 
        $s_node = $node_b;
        $s_port = $portb;
    }
    if ($ns_choice eq "C") { 
        $s_node = $node_c;
        $s_port = $portc;
    }

    # reset test, may use again
    $test = 0;
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    $user_pass = $diaser_passes[0]; 

# MARKER with double quotes - create run_listener.pl
open my $LISTENER, '>', 'run_listener.pl' 
    or croak "Can't create run_listener.pl: $OS_ERROR";
print $LISTENER <<"_MARKER_";
#!/usr/bin/perl -w
# Please do NOT edit this file
use strict;
use warnings;

system "./NPtcp";

exit 0;
_MARKER_
close $LISTENER or croak "can't close run_listner.pl: $OS_ERROR";

# MARKER with double quotes  - create run_sender.pl
open my $SENDER, '>', 'run_sender.pl'
    or croak "Can't create run_sender.pl: $OS_ERROR";
print $SENDER <<"_MARKER_";
#!/usr/bin/perl -w
# Please do NOT edit this file
use strict;
use warnings;

system "./NPtcp -h ${l_node}";

exit 0;
_MARKER_
close $SENDER;
 
    # reset permissions of files to be transferred 
    chmod 0770, "run_listener.pl", "run_sender.pl";
    
    # transfer NPtcp to listener node
    my $sftp = Net::SFTP->new("$l_node",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $l_port]) or die "Could not create SFTP connection";
    # Send files.
    $sftp->put("${nptcp_dir}/NPtcp","NPtcp"); 
    $sftp->put("run_listener.pl","run_listener.pl"); 
    print "NPtcp and run listener.pl transferred to listener\n";
    
    # transfer NPtcp to sender node
    $sftp = Net::SFTP->new("$s_node",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $s_port]) or die "Could not create SFTP connection";
    # Send files.
    $sftp->put("${nptcp_dir}/NPtcp","NPtcp"); 
    $sftp->put("run_sender.pl","run_sender.pl"); 
    print "NPtcp and run_sender.pl transferred to sender\n";

    # here I have redirected stdout and stderr to NPtcp.log to prevent
    # the sub-processes locking Net::SSH::Perl, this means the process
    # can background and the next process run

    # run NPtcp as listener
    my $ssh = Net::SSH::Perl->new("$l_node", debug => "0", 
    port => "$l_port"); 
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("./run_listener.pl > NPtcp.log 2>&1 &");
    print "NPtcp initiated on listener node\n";
    
    # run NPtcp as sender
    $ssh = Net::SSH::Perl->new("$s_node", debug => "0", 
    port => "$s_port"); 
    ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("./run_sender.pl > NPtcp.log 2>&1 &");
    print "NPtcp initiated on sender node\n";
    
    # this will take about 4 minutes
    # cheap and nasty progress bar, redo 
    my $cnt = 200;
    my $percent;
    my $i;
    my $k;

    for($i=0;$i<$cnt;$i++)
    {
        # calculate %
        $percent = ($i/$cnt)*100;

        for($k=0;$k<$percent;$k=$k+5)
        {
            print "|";
        }
     print " <-- $percent% Complete --> ";
        print "\r";
       sleep 1;
    }
    
    print "\n\n";
    
    # collect np.out
    $sftp = Net::SFTP->new("$s_node",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $s_port]) or die "Could not create SFTP connection";
    # Send files.
    $sftp->get("np.out","np.out"); 
    print "Stats collected from sender\n";

    # now make a gnuplot graph

# MARKER with double quotes  - create gnu-plot script
open my $GNUPLOT, '>', 'throughput.gplot'
    or croak "Can't open throughput.gplot: $OS_ERROR";
print $GNUPLOT <<"_MARKER_";
set logscale x
set xrange [1:1000000]
set ylabel "Actual bandwidth / throughput - Mbps"
set xlabel "test package size - Bytes"
set title "DIASER throughput test"
plot 'np.out' using 1:2 with lines
_MARKER_
close $GNUPLOT;

    system "gnuplot -persist throughput.gplot";        
    
    # ask if delete files?
    print "deleting output run_listener.pl run_sender.pl\n";
    system "rm run_listener.pl run_sender.pl";

    exit;
}

# calculate Lowest Max Bandwidth - LMB
sub calculate_lmb { 
 print "calc lbm not yet implemented\n";
 exit;
}

# remove extraneous files in the local directory created by diaser
sub clean { 
 print "tidying up...\n";
    print "deleting tab_{a,b,c}.pl\n";
    system "rm tab_a.pl tab_b.pl tab_c.pl";
    print "deleting hvautoc_{a,b,c}.pl\n";
    system "rm hvautoc_a.pl hvautoc_b.pl hvautoc_c.pl";
    print "deleting fill_diaser.pl\n";
    system "rm fill_diaser.pl";
    print "extraneous local files deleted\n";
    
    exit;
}

# check OS and Perl compatibility on nodes
sub compat_os_perl {   
 
    print "compat not yet implemented\n";
 exit;
}

# query driven configure tool for new and existing diaser deployments
sub configure {   
    
    # unpack scalar parameters
    my ($numyears, $startyear, $phase1, $phase2, $nodea, $nodeb, $nodec,
    $porta, $portb, $portc, $dryrun, $lmb, 
    $tzone_a, $tzone_b, $tzone_c, $useracc, $tout,
    $userdira, $userdirb, $userdirc, $use_sudo, $sudo_account_name_a,
    $sudo_account_name_b, $sudo_account_name_c, $fillstarttime, $volumedir, 
    $diffconstprefix, $collectfull, $collectfullday, $fullprefix, 
    $nummachines, $nummonths, $startmonth, $startdom) = @_;

    my $test = 0;
    
    # introduction
    print "\nThis tool will guide you through the configuration of\n";
    print "DIASER. Please answer the questions...\n\n";
    
    # start year
    while ($test == 0) {
        print "DIASER can also facilitate retrospective archiving.\n";
        print "From which date do you need to archive data?\n"; 
        print "(can be in the past)\n";
        print "[${startyear}]\n";
        # read input
        $startyear = <>;
        chomp $startyear; 
        # test if integer
            if(is_integer($startyear)) { 
                $test = 1;
            }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # how many years
    while ($test == 0) {
    print "\nHow many years of operation do you need? [${numyears}]\n";
    # read input
        $numyears = <>;
        chomp $numyears; 
    # test if integer
        if(is_integer($numyears)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # phase1
    while ($test == 0) {
    print "\nPhase 1 describes the period when the DIASER is filled,\n" 
    ."at your local timezone, internally on node 1 and transferred to node 2.\n" 
     ."What time do you want phase1 to begin?\n"
            ."0 = 0:00  23 = 23:00 [${phase1}]\n";
  # read input
        $phase1 = <>;
  chomp $phase1; 
  # test if integer
        if(is_integer_inc_zero($phase1)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # phase2
    while ($test == 0) {
    print "\nPhase 2 is the period when archives are copied from node 2\n"
    ."to node 3. What time do you want phase2 to begin?\n"
    ."0 = 0:00  23 = 23:00 [${phase2}]\n";
    # read input
        $phase2 = <>;
        chomp $phase2; 
    # test if integer
        if(is_integer_inc_zero($phase2)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer:\n\n";
        }
        # test not the same as phase1
        if($phase2 == $phase1) { 
            print "Phase2 value cannot be the same as Phase1:\n\n";
            $test = 0;
        }
        # ensure phase1 is greater than phase2 even if phase one is set 
        # before midnight
        # if($phase1 <= 23) { 
        # my $phase1_check = $phase1 + 23;
        # my $phase2_check = $phase2 + 23;
        # }
 }
    # reset test
    $test = 0;
    
    # node a ip address
    while ($test == 0) {
        print "\nWhat is the IP address of node A? [${nodea}]\n";
    # read input
        $nodea = <>;
        chomp $nodea; 
    # test if IP address
        if(is_ip($nodea)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an IP address:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # node b ip address
    while ($test == 0) {
        print "\nWhat is the IP address of node B? [${nodeb}]\n";
    # read input
        $nodeb = <>;
        chomp $nodeb; 
    # test if IP address
        if(is_ip($nodeb)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an IP address:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # node c ip address
    while ($test == 0) {
        print "\nWhat is the IP address of node C? [${nodec}]\n";
    # read input
        $nodec = <>;
        chomp $nodec; 
    # test if IP address
        if(is_ip($nodec)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an IP address:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # port of ssh on node a
    while ($test == 0) {
        print "\nWhat port does node A use for SSH? [${porta}]\n";
    # read input
        $porta = <>;
        chomp $porta; 
    # test if integer
        if(is_integer($porta)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # port of ssh on node b
    while ($test == 0) {
        print "\nWhat port does node B use for SSH? [${portb}]\n";
    # read input
        $portb = <>;
        chomp $portb; 
    # test if integer
        if(is_integer($portb)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # port of ssh on node c
    while ($test == 0) {
        print "\nWhat port does node C use for SSH? [${portc}]\n";
    # read input
        $portc = <>;
        chomp $portc; 
    # test if integer
        if(is_integer($portc)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # dryrun or not
    while ($test == 0) {
        print "\nDo you want to use dry run mode, no data is copied but logs\n"
        ."of what would be activity are still kept, 0 for no and 1 for yes? " 
        ."[${dryrun}]\n";
    # read input
        $dryrun = <>;
        chomp $dryrun; 
    # test if string
        if(is_string($dryrun)) { # is string for (bool)
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # lowest maximum bandwidth
    while ($test == 0) {
        print "\nLMB is the lowest maximum bandwidth between any three\n"
     ."of nodes chosen for use with DIASER.\n"
        ."What is your LMB between nodes in KBytes/s ? [${lmb}]\n";
    # read input
        $lmb = <>;
        chomp $lmb; 
    # test if integer
        if(is_integer($lmb)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
   
    # simple enough to ask UTC + $, I have prototyped a sub routine which will
    # interpret strings like CET/GMT/BST and handle conversions; sub tzone(); 

    # time zone for node a UTC + integer
    while ($test == 0) {
    print "\nPlease enter the time zone UTC +/- integer for node A,\n i.e."
         ." UTC integer value, if node A is BST = UTC 0" 
         ." (DST adjustment is automatic) [${tzone_a}]\n";
        # read input
        $tzone_a = <>;
                chomp $tzone_a; 
                # test if integer
        if(is_integer_plus_minus($tzone_a)) { 
                $test = 1;
        }
        else {
           print "Incorrect - please enter an integer\n";
        }       
    }
    # reset test
    $test = 0;
    
    # time zone for node b UTC + integer
    while ($test == 0) {
        print "\nPlease enter the time zone UTC +/- integer for node B,\n i.e."
         ." UTC integer value, if node B is BST = UTC 0" 
         ." (DST adjustment is automatic) [${tzone_b}]\n";
         # read input
        $tzone_b = <>;
                chomp $tzone_b; 
                # test if integer
        if(is_integer_plus_minus($tzone_b)) { 
                $test = 1;
        }
        else {
           print "Incorrect - please enter an integer\n";
        }       
    }
    # reset test
    $test = 0;
    
    # time zone for node c UTC + integer
    while ($test == 0) {
        print "\nPlease enter the time zone UTC +/- value for node C,\n i.e."
         ." UTC integer value, if node C is BST = UTC 0" 
         ." (DST adjustment is automatic) [${tzone_c}]\n";
         # read input
        $tzone_c = <>;
                chomp $tzone_c; 
                # test if integer
        if(is_integer_plus_minus($tzone_c)) { 
                $test = 1;
        }
        else {
           print "Incorrect - please enter an integer\n";
        }       
    }
    # reset test
    $test = 0;
    
    # user account name
    while ($test == 0) {
        print "\nWhat account name, this is the encasing user account name\n"
        ."used by all nodes, do you want to use on the nodes? [${useracc}]\n";
    # read input
        $useracc = <>;
        chomp $useracc; 
    # test if string
        if(is_string($useracc)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an alpha-numeric string:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # force a data transfer time out
    while ($test == 0) {
        print "\nIf DIASER data transfers become too slow or stall\n"
        ."due to network or other fail conditions, after how long\n" 
        ."should all data transfers time out in seconds? [${tout}]\n";
    # read input
        $tout = <>;
        chomp $tout; 
    # test if integer
        if(is_integer($tout)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # the directory on node A to create the diaser user account in 
    while ($test == 0) {
        print "\nWhat is the root directory where DIASER will create the\n" 
        ."encasing user account on node A? [${userdira}]\n";
    # read input
        $userdira = <>;
        chomp $userdira; 
    # test if directory
        if(is_directory($userdira)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter the full directory pathway"
            ." including leading and trailing /s:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # the directory on node B to create the diaser user account in 
    while ($test == 0) {
        print "\nWhat is the root directory where DIASER will create the\n"
        ."encasing user account on node B? [${userdirb}]\n";
    # read input
        $userdirb = <>;
        chomp $userdirb; 
    # test if directory
        if(is_directory($userdirb)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter the full directory pathway"
            ." including leading and trailing /s:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # the directory on node C to create the diaser user account in 
    while ($test == 0) {
        print "\nWhat is the root directory where DIASER will create the\n"
        ."encasing user account on node C? [${userdirc}]\n";
    # read input
        $userdirc = <>;
        chomp $userdirc; 
    # test if directory
        if(is_directory($userdirc)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter the full directory pathway"
            ." including leading and trailing /s:\n\n";
        } 
    }
    # reset test
    $test = 0;
    
    ### sudo mods here 05/02/10
    # these are instead of root
    # vars USE_SUDO[integer], SUDO_ACCOUNT_NAME_A[],B and C
    
    # use sudo or not
    while ($test == 0) {
        print "\nDo you want to use a sudo account,\n"
        ."0 for no and 1 for yes? " 
        ."[${use_sudo}]\n";
  # read input
        $use_sudo = <>;
        chomp $use_sudo; 
  # test if string
        if(is_string($use_sudo)) { # bool
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;

    if ($use_sudo == 1) {
    
        # sudo account name node A (repeated for nodes B and C)
        while ($test == 0) {
            print "\nWhat account name, this is the login account for sudo\n"
            ."for node A, do you want to use? [${sudo_account_name_a}]\n";
        # read input
            $sudo_account_name_a = <>;
            chomp $sudo_account_name_a; 
        # test if string
            if(is_string($sudo_account_name_a)) { 
                $test = 1;
            }
                else {
                    print "Incorrect - please enter an alpha-numeric"
                    ."string:\n\n";
                }
        }
        # reset test
        $test = 0;
        
        # sudo account name node B (repeated for nodes B and C)
        while ($test == 0) {
            print "\nWhat account name, this is the login account for sudo\n"
            ."for node B, do you want to use? [${sudo_account_name_b}]\n";
        # read input
            $sudo_account_name_b = <>;
            chomp $sudo_account_name_b; 
        # test if string
            if(is_string($sudo_account_name_b)) { 
                $test = 1;
            }
                else {
                    print "Incorrect - please enter an alpha-numeric"
                    ."string:\n\n";
                }
        }
        # reset test
        $test = 0;
        
        # sudo account name node C (repeated for nodes B and C)
        while ($test == 0) {
            print "\nWhat account name, this is the login account for sudo\n"
            ."for node C, do you want to use? [${sudo_account_name_c}]\n";
        # read input
            $sudo_account_name_c = <>;
            chomp $sudo_account_name_c; 
        # test if string
            if(is_string($sudo_account_name_c)) { 
                $test = 1;
            }
                else {
                    print "Incorrect - please enter an alpha-numeric"
                    ."string:\n\n";
                }
        }
        # reset test
        $test = 0;
    
    }
    
    # time to initiate the daily filling script 
    while ($test == 0) {
        print "\nAt this time DIASER will be filled with archives from a\n"
     ."specified directory. What time should fill_diaser.pl run?\n"
        ."0 = 0:00  23 = 23:00 [${fillstarttime}]\n";
        # read input
        $fillstarttime = <>;
        chomp $fillstarttime; 
        # test if directory
        if(is_integer_inc_zero($fillstarttime)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # location of volume storage directory
    while ($test == 0) {
        print "\nIn which directory should DIASER look for backup volumes?\n"
        ."fill_diaser.pl will pull (copy) archives from here [${volumedir}]\n";
    # read input
        $volumedir = <>;
        chomp $volumedir; 
    # test if directory
        if(is_directory($volumedir)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter the full directory pathway"
            ." including leading and trailing /s:\n\n";
        }
    }
    # reset test
    $test = 0;
 
    # differential or constant volume name prefix 
    while ($test == 0) {
        print "\nWhat is the differential or constant volume name prefix?\n"
        ."[${diffconstprefix}]\n";
    # read input
        $diffconstprefix = <>;
        chomp $diffconstprefix; 
    # test if directory
        if(is_string($diffconstprefix)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an alpha-numeric string:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # choose whether full volumes are collected or not 
    while ($test == 0) {
        print "\nDo you want DIASER to process Full volumes, 0 for no and 1\n"
        ."for yes? [${collectfull}]\n";
        # read input
        $collectfull = <>;
        chomp $collectfull; 
    # test if directory
        if(is_string($collectfull)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an alpha-numeric string:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # choose the day of month to collect full volumes
    while ($test == 0) {
        print "\nWhich day of the month should Full volumes to be collected?\n"
        ."1-28 [${collectfullday}]\n";
        # read input
        $collectfullday = <>;
        chomp $collectfullday; 
        # test if integer
        if(is_integer($collectfullday)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # full volume name prefix 
    while ($test == 0) {
        print "\nWhat is the full volume name prefix?"
        ." [${fullprefix}]\n";
    # read input
        $fullprefix = <>;
        chomp $fullprefix; 
        # test if directory
        if(is_string($fullprefix)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an alpha-numeric string:\n\n";
        }
    }
 
    # select yes to write to config file no to exit
    print "\nDo you want to write to diaser.conf?\n";
    my $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
   
    # call write_to_config() 
    write_to_config($numyears, $startyear, $phase1, $phase2, $nodea, $nodeb, 
    $nodec, $porta, $portb, $portc, $dryrun, $lmb, 
    $tzone_a, $tzone_b, $tzone_c, $useracc, $tout,
    $userdira, $userdirb, $userdirc, 
    $use_sudo, $sudo_account_name_a, $sudo_account_name_b,
    $sudo_account_name_c, $fillstarttime, $volumedir, 
    $diffconstprefix, $collectfull, $collectfullday, $fullprefix, 
    $nummachines, $nummonths, $startmonth, $startdom);
 
    # select yes to send modified settings to nodes or no to exit
    print "Please run diaser.pl --install\n";

    exit;
}

# create the diaser POSIX user accounts on nodes 
sub create_accounts { 
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, 
    $port_c, $user_acc, $dir_a, $dir_b, $dir_c, $use_sudo, 
    $sudo_account_name_a, $sudo_account_name_b, $sudo_account_name_c) = @_;

    # plain-text and crypt password generated by ask_diaser_pass()
    # also ask for a password check by using parameter scalar 1
    # this password is the same for all three DIASER accounts generated 
    my @diaser_passes = ask_diaser_pass(1);
    $user_pass = $diaser_passes[0]; 
    my $crypt_user_pass = $diaser_passes[1]; 
   
    if ($use_sudo == 0) {
    
        # pop the root passwords generated by ask_node_pass()
        my @root_passes = ask_node_pass();
        my $root_pass_node_c = pop(@root_passes); 
        my $root_pass_node_b = pop(@root_passes); 
        my $root_pass_node_a = pop(@root_passes); 
   
        # adduser to node commands
        my $ADD_USER_A="/usr/sbin/useradd -g users -G sshd -d ${dir_a}"
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";
        my $ADD_USER_B="/usr/sbin/useradd -g users -G sshd -d ${dir_b}"
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";
        my $ADD_USER_C="/usr/sbin/useradd -g users -G sshd -d ${dir_c}"
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";
        
        # if the group account is ssh not sshd i.e. for Debian based distros
        # S for short group name
        my $ADD_USER_S_A="/usr/sbin/useradd -g users -G ssh -d ${dir_a}"
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";
        my $ADD_USER_S_B="/usr/sbin/useradd -g users -G ssh -d ${dir_b}"
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";
        my $ADD_USER_S_C="/usr/sbin/useradd -g users -G ssh -d ${dir_c}"
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";

        # command to check node distribution
        my $ISSUE="/bin/cat /etc/issue";

        # setup diaser user on node A
        my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", 
        port => "$port_a");
        my ($return) = $ssh->login("root", "$root_pass_node_a");
       
        # test /etc/issue
        my($stdout, $stderr, $exit) = $ssh->cmd("$ISSUE");
        if($stdout =~ /Debian/i) {
                $ssh->cmd("$ADD_USER_S_A");
        } else {
                $ssh->cmd("$ADD_USER_A");
        }
        print "User ${user_acc} added to node A\n";
 
        # setup diaser user on node B
        $ssh = Net::SSH::Perl->new("$node_b", debug => "0", 
        port => "$port_b");
        ($return) = $ssh->login("root", "$root_pass_node_b");
        
        # test /etc/issue
        ($stdout, $stderr, $exit) = $ssh->cmd("$ISSUE");
        if($stdout =~ /Debian/i) {
                $ssh->cmd("$ADD_USER_S_B");
        } else {
                $ssh->cmd("$ADD_USER_B");
        }
        print "User ${user_acc} added to node B\n";
        
        # setup diaser user on node C
        $ssh = Net::SSH::Perl->new("$node_c", debug => "0", 
        port => "$port_c");
        ($return) = $ssh->login("root", "$root_pass_node_c");
        
        # test /etc/issue
        ($stdout, $stderr, $exit) = $ssh->cmd("$ISSUE");
        if($stdout =~ /Debian/i) {
                $ssh->cmd("$ADD_USER_S_C");
        } else {
                $ssh->cmd("$ADD_USER_C");
        }
        print "User ${user_acc} added to node C\n";
    
    } else {
            
        # request sudo user account password
        # plain-text and crypt password generated by ask_sudo_pass()
        # also ask for a password check by using parameter scalar 1
        
        ## need to make adjustments for three sudo accounts, including ask for
        # the name of three accounts using the variables 04/02/10
        # ask names of accounts by adding this configuration in sub
        # configure()
        
        my @sudo_passes = ask_sudo_pass(1);
        
        $sudo_pass_a = $sudo_passes[0]; 
        my $crypt_sudo_pass_a = $sudo_passes[1];
        
        $sudo_pass_b = $sudo_passes[2]; 
        my $crypt_sudo_pass_b = $sudo_passes[3];
        
        $sudo_pass_c = $sudo_passes[4]; 
        my $crypt_sudo_pass_c = $sudo_passes[5];
        
        # adduser with sudo to node commands
        # root password -S on sudo
        my $ADD_USER_A="echo ${sudo_pass_a} | sudo -S" 
        ." /usr/sbin/useradd -g users -G ssh -d ${dir_a}" 
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";
        my $ADD_USER_B="echo ${sudo_pass_b} | sudo -S"
        ." /usr/sbin/useradd -g users -G ssh -d ${dir_b}"
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";
        my $ADD_USER_C="echo ${sudo_pass_c} | sudo -S"
        ." /usr/sbin/useradd -g users -G ssh -d ${dir_c}"
        ."${user_acc} -m -p ${crypt_user_pass} ${user_acc}";
   
        # setup diaser user on node A
        my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", 
        port => "$port_a");
        my ($return) = $ssh->login("$sudo_account_name_a", 
        "$sudo_pass_a");
        $ssh->cmd("$ADD_USER_A");
        print "User ${user_acc} added to node A\n";
 
        # setup diaser user on node B
        $ssh = Net::SSH::Perl->new("$node_b", debug => "0", 
        port => "$port_b");
        ($return) = $ssh->login("$sudo_account_name_b", 
        "$sudo_pass_b");
        $ssh->cmd("$ADD_USER_B");
        print "User ${user_acc} added to node B\n";
 
        # setup diaser user on node C
        $ssh = Net::SSH::Perl->new("$node_c", debug => "0", 
        port => "$port_c");
        ($return) = $ssh->login("$sudo_account_name_c", 
        "$sudo_pass_c");
        $ssh->cmd("$ADD_USER_C");
        print "User ${user_acc} added to node C\n";
    }

    return;
}

# extend maximum storage structure date 
sub extend {   
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, $port_c, $user_acc) = @_;
    
    # calculate the start date+1, display and (ask user to confirm?) then 
    # request new end date then call sub gen_dirs() with new dates
    
    my $test = 0;
    my $numyears;
    my $startyear;
    
    # introduction
    print "\nThis tool will allow you to extend the number of years\n";
    print "of DIASER operation. Please answer the questions...\n\n";
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    $user_pass = $diaser_passes[0]; 
    
    # start year
    while ($test == 0) {
     print "From which year do you need extend operation?\n\n"; 
     print "Please check the furthest year in the future you have already\n"; 
     print "installed on your nodes then choose the next year.\n\n"; 
     print "IMPORTANT - please ensure the date you choose is correct\n"; 
     print "to avoid the risk of overwriting previous storage volumes.\n"; 
    # read input
        $startyear = <>;
        chomp $startyear; 
    # test if integer
        if(is_integer($startyear)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # how many years
    while ($test == 0) {
        print "\nHow many extra years of operation do you need?\n";
    # read input
        $numyears = <>;
        chomp $numyears; 
    # test if integer
        if(is_integer($numyears)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    
    gen_dirs($numyears, $startyear, $config->NODE_A, 
    $config->NODE_B, $config->NODE_C, $config->PORT_A, $config->PORT_B, 
    $config->PORT_C, $config->USER_ACC, $config->DIR_A, $config->DIR_B, 
    $config->DIR_C);
    
    print "extend operation complete\n";
    
 exit;
}
    
# create the directory structure on chosen nodes, make number of years 
# starting at $start_year defined in diaser.conf then populate with months 
# then make days  
sub gen_dirs { 

    # unpack scalar parameters
    my ($num_years, $start_year, $node_a, $node_b, $node_c, 
    $port_a, $port_b, $port_c, $user_acc, $dir_a, $dir_b, $dir_c) = @_;
    
    # year calculation using start year defined in diaser.conf
    # touch log at ~/ when making years
    my $year = $start_year;
    my $MAKE_YEARS="mkdir ${year}";
    
    # start in January, may as well, operation start date may differ
    my $month=1; 
 
    # month directory definitions
    my $MAKE_MONTHS="cd ${year} && mkdir mth1 mth2 mth3 mth4 mth5 mth6 mth7" 
    ." mth8 mth9 mth10 mth11 mth12"; 
 
    # each occupies a month, max 31 days covers variable month length, 
    # d stands for day
    my $MAKE_DAYS="cd ${year} && cd mth${month} && mkdir Full01 Full02" 
    ." d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19"
    ." d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31";
 
    # loop through n*chosen years, create years then populate with months 
    while ($num_years > 0) {
     
    # setup dirs on node A
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", 
        port => "$port_a");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
        $ssh->cmd("$MAKE_YEARS");
    $ssh->cmd("$MAKE_MONTHS");
    print "Year ${year} and months created on node A\n";
 
    # setup dirs on node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", 
        port => "$port_b");
     ($return) = $ssh->login("$user_acc", "$user_pass");
        $ssh->cmd("$MAKE_YEARS");
     $ssh->cmd("$MAKE_MONTHS");
     print "Year ${year} and months created on node B\n";                  
        
        # setup dirs on node C
     $ssh = Net::SSH::Perl->new("$node_c", debug => "0", 
        port => "$port_c");
     ($return) = $ssh->login("$user_acc", "$user_pass");
        $ssh->cmd("$MAKE_YEARS");
     $ssh->cmd("$MAKE_MONTHS");
     print "Year ${year} and months created on node C\n";
   
  # populate the months with day slots 
  print "Populating ${year} months with day slots...\n";
  # 12 iterations for each node per year
  while ($month < 13) {
   
            my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", 
            port => "$port_a");
         my ($return) = $ssh->login("$user_acc", "$user_pass");
            $ssh->cmd("$MAKE_DAYS");
   
            $ssh = Net::SSH::Perl->new("$node_b", debug => "0", 
            port => "$port_b");
         ($return) = $ssh->login("$user_acc", "$user_pass");
   $ssh->cmd("$MAKE_DAYS");
 
   $ssh = Net::SSH::Perl->new("$node_c", debug => "0", 
            port => "$port_c");
         ($return) = $ssh->login("$user_acc", "$user_pass");
   $ssh->cmd("$MAKE_DAYS");
   
            $month += 1; # increment the month

            # re-declare $MAKE_DAYS
            $MAKE_DAYS="cd ${year} && cd mth${month} && mkdir Full01" 
            ." Full02 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15" 
            ." d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30"
            ." d31";
        
        }
  print "Complete...\n";
  
  $year += 1; # increment the year
  $month = 1; # reset month
 
        # re-declare $MAKE_YEARS, $MAKE_MONTHS and $MAKE_DAYS
        $MAKE_YEARS="mkdir ${year}"; 
 
     $MAKE_MONTHS="cd ${year} && mkdir mth1 mth2 mth3 mth4 mth5 mth6" 
        ." mth7 mth8 mth9 mth10 mth11 mth12"; 
 
        $MAKE_DAYS="cd ${year} && cd mth${month} && mkdir Full01 Full02" 
        ." d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18"
        ." d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31"; 
        
        $num_years -= 1; # decrement the year count 
 } # end while

    return;
}

# generate the fill_diaser.pl script called by cron, also generate an 
# updated cron tab file, tab_a.pl, see fill_diaser.pl flow chart
sub gen_fill {

# unpack scalar parameters
my ($tzone_a, $user_acc, $dir_a, $fillstarttime, $volumedir, $diffconstprefix, 
$collectfull, $collectfullday, $fullprefix) = @_;

# MARKER with double quotes 
open my $FILLSCRIPTHEAD, '>', 'fill_diaser.pl'
    or croak "Can't create fill_diaser.pl: $OS_ERROR";
print $FILLSCRIPTHEAD <<"_MARKER_";
#!/usr/bin/perl -w
# Please do NOT edit this file
use strict;
use warnings;
use File::Find;

my \$tz_a       = "$tzone_a";           # time zone compensation variable
my \$dhome      = "$user_acc";          # diaser home directory   
my \$hdir       = "$dir_a";             # node home directory   
my \$vdir       = "$volumedir";         # volume directory 
my \$dcprefix   = "$diffconstprefix"; # differential or constant prefix
my \$cfull      = "$collectfull";     # collect full volumes?
my \$cfulld     = "$collectfullday"; # day of month to collect full volume
my \$fprefix    = "$fullprefix";        # full volume prefix

_MARKER_
close $FILLSCRIPTHEAD;

# MARKER with single quotes 
open my $FILLSCRIPTBODY, '>>', 'fill_diaser.pl'
    or croak "Can't open fill_diaser.pl: $OS_ERROR";
print $FILLSCRIPTBODY <<'_MARKER_';

# flush all output
$| = 1; 

my $new_copy = 1;

(my $lsec,my $lmin,my $lhour,my $lmday,my $lmon,my $lyear,my $lwday,my $lyday,
my $lisdst) = localtime(time);

# time zone compensation UTC, as yet unused
# if ($lisdst) { $tz_a+=1; } # if daylight saving +1

$lyear+=1900;
$lmon+=1; # to adjust for month directory naming scheme

# logging
system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_fill]" 
." cron ran fill_diaser.pl' | cat >> log_a"; 

# check if yesterdays A->B copy was successful
# implement


# check lmday for Full copies
if (($cfull) && ($lmday == $cfulld)) { # process full volume
    
    # ensure new_copy is true
    $new_copy = 1;

    # change to user specified volume directory
    chdir $vdir or die "Can't change to specified directory: $!";
    system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_fill]" 
    ." changed to ${vdir}' | cat >> ~/log_a";
    
    # find most recent full volume, using high-water-mark algorithm
    my @files = <*>;
    my @fulls;

    # match the full prefix in current directory then load into @fulls
    foreach (@files) {
     if ($_ =~ /^($fprefix)/) {
     unshift @fulls, $_; 
     }
    }

    # using high-water-mark algorithm 
    # how many elements in array? if empty then quit
    my $is_empty = @fulls;
    if ($is_empty == 0) { 
        system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_fill]" 
        ." no full volumes stored' | cat >> ~/log_a";
        exit; 
    }

    my $newest_name = shift @fulls;
    my $newest_age = -M $newest_name;
    foreach (@fulls) {
        my $age = -M;
     if ($age < $newest_age) {
            ($newest_name, $newest_age) = ($_, $age)
     }
    }

    # has this volume been collected before?
   
    # search home directory for matching file   
    chdir $hdir.$dhome or die "Can't change to specified directory: $!";
    find(\&matchfull, $hdir.$dhome);
    
    # match against name, as the volume name may have been used previously
    # Check to see if the volume has been collected before, if the volume name
    # has then check to see if it is the same volume we are considering moving
    # into DIASER by comparing size. If the volume is a different size then
    # although the name has been used before it is a different file, so we can
    # move the latest volume into DIASER. To ensure the correct files are
    # definitely being compared, for size -s, I ah using the full pathway for
    # the existing volume and the new volume, i.e. new volume $newest_file and
    # old volume with the same name/label $File::Find::name.

    sub matchfull {
        # is there a volumes name match?
        if (($_ eq $newest_name) && 
        # and if so do the volumes sizes match?
        (-s $File::Find::name == -s $vdir.$newest_name)) {

        # if the volume size is the same then don't make a copy
        $new_copy = 0;
        }
    }
    
    if ($new_copy) {

        # copy to /year/month/FULL01
        system "cp ${vdir}${newest_name}  ${lyear}/mth${lmon}/Full01/";
        
        # log copy
        system "echo '[diaser_fill] ${vdir}${newest_name} copied to"
        ." ${lyear}/mth${lmon}/Full01/${newest_name}' | cat >> ~/log_a";

        # create sha1sum
        system "sha1sum ${lyear}/mth${lmon}/Full01/${newest_name}"
        ." > ${lyear}/mth${lmon}/Full01/${newest_name}.sha1";

        # date stamp
        system "date | cat >> ${lyear}/mth${lmon}/Full01/${newest_name}.sha1";
    }

} else { # this part is for differential or constant volumes
    
    # ensure new_copy is true
    $new_copy = 1;

    # find most recent diff or constant volume
    
    # change to user specified volume directory
    chdir $vdir or die "Can't change to specified directory: $!";
    system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_fill]" 
    ." changed to ${vdir}' | cat >> ~/log_a";

    # find most recent full volume, using high-water-mark algorithm
    my @files = <*>;
    my @diffcs;

    # match the diff const prefix in current directory then load into @diffcs
    foreach (@files) {
     if ($_ =~ /^($dcprefix)/) {
     unshift @diffcs, $_; 
     }
    }
    
    # how many elements in array? if empty then quit
    my $is_empty = @diffcs;
    if ($is_empty == 0) { 
    system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_fill]" 
        ." no differential volumes stored' | cat >> ~/log_a";
        exit; 
    }

    # using high-water-mark algorithm 
    my $newest_name = shift @diffcs;
    my $newest_age = -M $newest_name;
    foreach (@diffcs) {
        my $age = -M;
     if ($age < $newest_age) {
            ($newest_name, $newest_age) = ($_, $age)
     }
    }
    
    # change back to the home directory
    chdir $hdir or die "Can't change to specified directory: $!";

    # has this volume been collected before?
    
    # search home directory for matching file   
    chdir $hdir.$dhome or die "Can't change to specified directory: $!";
    
    # delete contents of d0
    system "rm ${lyear}/mth${lmon}/d0/*";
 
    find(\&matchdiffc, $hdir.$dhome);

    # match against name, as the volume name may have been used previously
    # another check is required
    sub matchdiffc {
        # is there a volumes name match?
        if (($_ eq $newest_name) && 
        # and if so do the volumes sizes match?
        (-s $File::Find::name == -s $vdir.$newest_name)) {

        # if the volume size is the same then don't make a copy
        $new_copy = 0;
        }
    }
    
    # if copy is true then transfer volume to /year/month/d0
    if ($new_copy) {

        # copy to /year/month/d0
        system "cp ${vdir}${newest_name}  ${lyear}/mth${lmon}/d0/";
        
        # log copy
        system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_fill]" 
        ." ${vdir}${newest_name} copied to"
        ." ${lyear}/mth${lmon}/d0/${newest_name}' | cat >> ~/log_a";

        # create sha256sum
        system "sha256sum ${lyear}/mth${lmon}/d0/${newest_name}"
        ." > ${lyear}/mth${lmon}/d0/${newest_name}.sha256sum";

        # date stamp
        system "date | cat >> ${lyear}/mth${lmon}/d0/${newest_name}.sha256sum";
    }
}        
    
_MARKER_
close $FILLSCRIPTBODY;

# MARKER with double quotes to interpolate variables
open my $FILLTIME, '>', 'tab_a.pl'
    or croak "Can't create tab_a.pl: $OS_ERROR";
print $FILLTIME <<"_MARKER_";
0 * * * * ~/hvautoc_a.pl
0 ${fillstarttime} * * * ~/fill_diaser.pl

_MARKER_
close $FILLTIME;

# execute perms
system "chmod +x fill_diaser.pl";
system "chmod +x tab_a.pl";

return;

}

# generate hypervirtual autochangers
sub gen_hvautoc {   

=pod
When the operational strategy requirements change the user can force a newly
generated algorithm into each of the three nodes.

This is the algol that needs to appear to be 'printed' out to each 
hvautoc_a,b,c.pl which are in turn copied to each node, after initial setup 
or later changes, tick every hour generated by cron. 

These variables are the local time with which to match against, 
prefix l = local, no need to adjust for different number of days in a month, 
this just happens, dry run mode is off by default.
=cut
    
# unpack scalar parameters
my ($num_years, $start_year, $hour1, $hour2, $node_a, $node_b, $node_c, 
$port_a, $port_b, $port_c, $dry_run, $low_max_bw, 
$tzone_a, $tzone_b, $tzone_c, $user_acc, $tout, 
$num_months, $start_dom, $start_month, $collectfullday) = @_;

open my $HVA_A_HEAD, '>', 'hvautoc_a.pl'
    or croak "Can't create hvautoc_a.pl: $OS_ERROR";
print $HVA_A_HEAD <<"_MARKER_";
#!/usr/bin/perl -w
# Please do NOT edit this file
use strict;
use warnings;
my \$user = "$user_acc"; # user account name as set in diaser.conf
my \$months = $num_months; # number of months expected operation
my \$years = $num_years;  # number of years of expected operation
my \$begin_dom = $start_dom; # first day of month of operation
my \$begin_month = $start_month;# first month of operation
my \$begin_year = $start_year; # first year of operation
my \$phase1 = $hour1;  # start time (phase1) first set copies 
my \$phase2 = $hour2;  # start time (phase2) second set of copies
my \$time_out = $tout;  # rsync copy time out in seconds, default = 5 hours
my \$lmb = $low_max_bw; # lowest maximum bandwidth KB/s
my \$tz_a = $tzone_a;   # time zone compensation variable
my \$na = "$node_a";    # node a
my \$nb = "$node_b";    # node b
my \$nc = "$node_c";    # node c
my \$pa = "$port_a";    # port a, unused but here in case required
my \$pb = "$port_b";    # port b
my \$pc = "$port_c";    # port c
my \$dr = $dry_run;     # is dry_run set, default 0=no, 1=yes
my \$cfulld = "$collectfullday"; # dom set by user to replicate full volumes

_MARKER_
close $HVA_A_HEAD;

open my $HVA_A_BODY, '>>', 'hvautoc_a.pl'
    or croak "Can't open hvautoc_a.pl: $OS_ERROR";
print $HVA_A_BODY <<'_MARKER_';

# rsync options
my $options="-azv";

# dry run mode set by user
if ($dr == 0) {
 $options="-azv";
}
else { $options="-anzv"; } 

(my $lsec,my $lmin,my $lhour,my $lmday,my $lmon,my $lyear,my $lwday,my $lyday,
my $lisdst) = localtime(time);

# tzone compensation UTC
# if ($lisdst) { $tz_a+=1; } # if daylight saving +1 hour

# if negative the make positive
# if the offset is positive the negate the offset
#my $new;
#if ($tz_a <= 0) {
#        $new = $tz_a*-1;
#        $tz_a = $new;
#} else {
#        $new = -$tz_a;
#        $tz_a = $new;
#}

$phase1+=$tz_a;
$phase2+=$tz_a;

#  if the sum of the chosen phase value and the tz compensation value is -ve 
#  i.e. (New York tz is -5) + (phase1 is 04:00 Wednesday) = -1 
#  or Tusday 23:00 NY time.
#  Therefore the compensation needs to result in a real time, i.e. 23:00
#  The calculation required to adjust for this is requirement is
#  if phaseX plus tz{node} is negative then the node time is 24-phaseX

if ($phase1 < 0) { 
        my $neg_phase = $phase1;
        $phase1 = 24 + $neg_phase;
}

if ($phase2 < 0) { 
        my $neg_phase = $phase2;
        $phase2 = 24 + $neg_phase;
}

$lyear+=1900; # to make 21st century
$lmon+=1; # to adjust for month directory naming scheme

# logging
system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_hvautoc_a]" 
." cron ran hvautoc_a.pl' | cat >> log_a"; 

# monthly cycle definitions of all copy types, node A...
# day 1 onwards
my $COPY_DOM_TYPE1="rsync ${options} -e 'ssh -p ${pb}' --timeout=$time_out" 
." --numeric-ids --stats --ignore-errors --bwlimit=$lmb" 
." ~/${lyear}/mth${lmon}/Full01/"
." ${user}\@${nb}:${lyear}/mth${lmon}/Full01/ >> log_a";

# day 1 onwards 
my $COPY_DOM_TYPE2="rsync ${options} --timeout=$time_out --numeric-ids" 
." --delete --stats --ignore-errors ~/${lyear}/mth${lmon}/d0/" 
." ~/${lyear}/mth${lmon}/d${lmday}/ >> log_a";

my $COPY_DOM_TYPE3="rsync ${options} -e 'ssh -p ${pb}' --timeout=$time_out" 
." --numeric-ids --delete --stats --ignore-errors --bwlimit=$lmb" 
." ~/${lyear}/mth${lmon}/d0/" 
." ${user}\@${nb}:${lyear}/mth${lmon}/d${lmday}/ >> log_a";

# skip if not the last month
my $COPY_DOM_TYPE4="rsync ${options} --timeout=$time_out" 
." --remove-source-files --numeric-ids --stats --ignore-errors" 
." ~/${lyear}/mth${lmon}/Full01/" 
." ~/${lyear}/mth${lmon}/Full02/ >> log_a";

# Operational as set by user - add more error handling, void context
if (($phase1 == $lhour) && ($lmday == $cfulld)) { 
    # Full copy from A->B Full01->Full01
 system $COPY_DOM_TYPE1; 
    # FULL from yr/mnth/Full01->../../Full01 on user selected day of month
}

if ($phase1 == $lhour) { 
 system $COPY_DOM_TYPE2; # internal copies, i.e. a0->a1, a0->a2...
 system $COPY_DOM_TYPE3; # diff copies from A->B, i.e. a0->b1, a0->b2...
}

# test for last day of the month #############################################
my $last_day=0; # is this the last day of the month yes=1, no=0
my $leap=0; # leap year, yes=1 no=0

# leap year or not
if (($lyear%4 == 0) && ($lyear%100 != 100) || ($lyear%400 == 0)) {
 $leap=1; 
}

# arrays of days in a month
my @days31 = qw( 1 3 5 7 8 10 12 );
my @days30 = qw( 4 6 9 11 );
my @days28 = qw( 2 );

# how many days has this month: $lmon
foreach my $days31 (@days31) {
 if (($lmon == $days31) && ($lmday == 31)) { 
  $last_day=1;
 }
}

foreach my $days30 (@days30) {
 if (($lmon == $days30) && ($lmday == 30)) { 
  $last_day=1;
 }
}

foreach my $days28 (@days28) { 
 if ($leap == 0) {
  if (($lmon == $days28) && ($lmday == 28)) { 
   $last_day=1;
  }
 }
 if ($leap == 1) {
  if (($lmon == $days28) && ($lmday == 29)) { 
   $last_day=1;
  }
 }
}
##############################################################################

# if not running for more that 1 year and at the end of the month, 
# day 28(29), 30 or 31
if (($years < 1) && ($phase1 == $lhour) && ($last_day)) { 
    system $COPY_DOM_TYPE4;
}

_MARKER_
close $HVA_A_BODY;

# Node B
open my $HVA_B_HEAD, '>', 'hvautoc_b.pl'
    or croak "Can't create hvautoc_b.pl: $OS_ERROR";

print $HVA_B_HEAD <<"_MARKER_";
#!/usr/bin/perl -w
# Please do NOT edit this file
use strict;
use warnings;
my \$user = "$user_acc"; # user account name as set in diaser.conf
my \$months = $num_months; # (may not need) num months expected operation
my \$years = $num_years;  # number of years of expected operation
my \$begin_dom = $start_dom; # first day of month of operation
my \$begin_month = $start_month; # first month of operation
my \$begin_year = $start_year; # first year of operation
my \$phase1 = $hour1;  # start time (phase1) first set copies 
my \$phase2 = $hour2;  # start time (phase2) second set of copies
my \$time_out = $tout; # rsync copy time out in seconds, default = 5 hours
my \$lmb = $low_max_bw; # lowest maximum bandwidth KB/s
my \$tz_b = $tzone_b;   # time zone compensation variable
my \$na = "$node_a"; # node a
my \$nb = "$node_b"; # node b
my \$nc = "$node_c"; # node c
my \$pa = "$port_a";    # port a
my \$pb = "$port_b";    # port b
my \$pc = "$port_c";    # port c
my \$dr = $dry_run;  # is dry_run set, default 0=no, 1=yes
my \$cfulld = "$collectfullday"; # dom to replicate full volumes as set 

_MARKER_
close $HVA_B_HEAD;

open my $HVA_B_BODY, '>>', 'hvautoc_b.pl'
    or croak "Can't open hvautoc_b.pl: $OS_ERROR";
print $HVA_B_BODY <<'_MARKER_';

# rsync options
my $options="-azv";

# dry run mode set by user
if ($dr == 0) {
 $options="-azv";
}
else { $options="-anzv"; } 

(my $lsec,my $lmin,my $lhour,my $lmday,my $lmon,my $lyear,my $lwday,
my $lyday,my $lisdst) = localtime(time);

# tzone compensation UTC
# if ($lisdst) { $tz_b+=1; } # if daylight saving +1 hour 

# if negative the make positive
# if the offset is positive the negate the offset

#my $new;
#if ($tz_b <= 0) {
#        $new = $tz_b*-1;
#        $tz_b = $new;
#} else {
#        $new = -$tz_b;
#        $tz_b = $new;
#}

$phase1+=$tz_b;
$phase2+=$tz_b;

#  if phaseX plus tz{node} is negative then the node time is 24-phaseX

if ($phase1 < 0) { 
        my $neg_phase = $phase1;
        $phase1 = 24 + $neg_phase;
}

if ($phase2 < 0) { 
        my $neg_phase = $phase2;
        $phase2 = 24 + $neg_phase;
}


$lyear+=1900;
$lmon+=1; # to adjust for month directory naming scheme

# adjust for phase 2 occurring after midnight, 
# so lmday -1 if phase 1 > phase 2
if ($phase1 > $phase2) {
        $lmday -=1;
        $cfulld -=1;
}

# logging
system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_hvautoc_b]" 
." cron ran hvautoc_b.pl' | cat >> log_b"; 

# monthly cycle definitions of all copy types, node B...
# day 1 onwards
my $COPY_DOM_TYPE1="rsync ${options} -e 'ssh -p ${pc}' --timeout=$time_out" 
." --numeric-ids --stats --ignore-errors --bwlimit=$lmb" 
." ~/${lyear}/mth${lmon}/Full01/" 
." ${user}\@${nc}:${lyear}/mth${lmon}/Full01/ >> log_b";

# day 1 onwards
my $COPY_DOM_TYPE2="rsync ${options} -e 'ssh -p ${pc}' --timeout=$time_out" 
." --numeric-ids --delete --stats --ignore-errors" 
." ~/${lyear}/mth${lmon}/d${lmday}/" 
." ${user}\@${nc}:${lyear}/mth${lmon}/d${lmday}/ >> log_b";

# skip if not the last month
my $COPY_DOM_TYPE4="rsync ${options} --timeout=$time_out" 
." --remove-source-files --numeric-ids --stats --ignore-errors" 
." ~/${lyear}/mth${lmon}/Full01/" 
." ~/${lyear}/mth${lmon}/Full02/ >> log_b";

# Operational as set by user
if (($phase2 == $lhour) && ($lmday == $cfulld)) {
        system $COPY_DOM_TYPE1; 
        # FULL from bFull01->cFull01 as set by user $cfulld
}

# Copies a diff as well as a Full as above
if ($phase2 == $lhour) {
        system $COPY_DOM_TYPE2; # copies, i.e. b0->c1, b0->b2...
}

# test for last day of the month #############################################
my $last_day=0; # is this the last day of the month yes=1, no=0
my $leap=0; # leap year, yes=1 no=0

# leap year or not
if (($lyear%4 == 0) && ($lyear%100 != 100) || ($lyear%400 == 0)) {
 $leap=1; 
}

# arrays of days in a month
my @days31 = qw( 1 3 5 7 8 10 12 );
my @days30 = qw( 4 6 9 11 );
my @days28 = qw( 2 );

# how many days has this month: $lmon
foreach my $days31 (@days31) {
 if (($lmon == $days31) && ($lmday == 31)) { 
  $last_day=1;
 }
}

foreach my $days30 (@days30) {
 if (($lmon == $days30) && ($lmday == 30)) { 
  $last_day=1;
 }
}

foreach my $days28 (@days28) { 
 if ($leap == 0) {
  if (($lmon == $days28) && ($lmday == 28)) { 
   $last_day=1;
  }
 }
 if ($leap == 1) {
  if (($lmon == $days28) && ($lmday == 29)) { 
   $last_day=1;
  }
 }
}
##############################################################################

# if not running for more that 1 year and at the end of the month, 
# day 28(29), 30 or 31
if (($years < 1) && ($phase1 == $lhour) && ($last_day)) { 
    system $COPY_DOM_TYPE4;
}

_MARKER_
close $HVA_B_BODY;

# Node C 
open my $HVA_C_HEAD, '>', 'hvautoc_c.pl'
    or croak "Can't create hvautoc_c.pl: $OS_ERROR";
print $HVA_C_HEAD <<"_MARKER_";
#!/usr/bin/perl -w
# Please do NOT edit this file
use strict;
use warnings;
my \$user = "$user_acc"; # user account name as set in diaser.conf
my \$months = $num_months; # (may not need) num months expected operation
my \$years = $num_years;  # number of years of expected operation
my \$begin_dom = $start_dom; # first day of month of operation
my \$begin_month = $start_month; # first month of operation
my \$begin_year = $start_year; # first year of operation
my \$phase1 = $hour1;  # start time (phase1) first set copies 
my \$phase2 = $hour2;  # start time (phase2) second set of copies
my \$time_out = $tout; # rsync copy time out in seconds, default = 5 hours
my \$lmb = $low_max_bw; # lowest maximum bandwidth KB/s
my \$tz_c = $tzone_c;   # time zone compensation variable
my \$na = "$node_a"; # node a
my \$nb = "$node_b"; # node b
my \$nc = "$node_c"; # node c
my \$pa = "$port_a";    # port a
my \$pb = "$port_b";    # port b
my \$pc = "$port_c";    # port c
my \$dr = $dry_run;     # is dry_run set, default 0=no, 1=yes

_MARKER_
close $HVA_C_HEAD;

open my $HVA_C_BODY, '>>', 'hvautoc_c.pl'
    or croak "Can't create hvautoc_c.pl: $OS_ERROR";
print $HVA_C_BODY <<'_MARKER_';

# rsync options
my $options="-azv";

# dry run mode set by user
if ($dr == 0) {
 $options="-azv";
}
else { $options="-anzv"; } 

(my $lsec,my $lmin,my $lhour,my $lmday,my $lmon,my $lyear,my $lwday,my $lyday,
my $lisdst) = localtime(time);

# tzone compensation UTC
# if ($lisdst) { $tz_c+=1; } # if daylight saving +1 hour

# if negative the make positive
# if the offset is positive the negate the offset

#my $new;
#if ($tz_c <= 0) {
#        $new = $tz_c*-1;
#        $tz_c = $new;
#} else {
#        $new = -$tz_c;
#        $tz_c = $new;
#}

$phase1+=$tz_c;
$phase2+=$tz_c;

#  if phaseX plus tz{node} is negative then the node time is 24-phaseX

if ($phase1 < 0) { 
        my $neg_phase = $phase1;
        $phase1 = 24 + $neg_phase;
}

if ($phase2 < 0) { 
        my $neg_phase = $phase2;
        $phase2 = 24 + $neg_phase;
}

$lyear+=1900;
$lmon+=1; # to adjust for month directory naming scheme

# logging
system "echo '${lmday}:${lmon}:${lyear}:${lhour}:${lmin} [diaser_hvautoc_c]" 
." cron ran hvautoc_c.pl' | cat >> log_c"; 

# skip if not the last month
my $COPY_DOM_TYPE4="rsync ${options} --timeout=$time_out" 
." --remove-source-files --numeric-ids --stats --ignore-errors" 
." ~/${lyear}/mth${lmon}/Full01/" 
." ~/${lyear}/mth${lmon}/Full02/ >> log_c";

# test for last day of the month #############################################
my $last_day=0; # is this the last day of the month yes=1, no=0
my $leap=0; # leap year, yes=1 no=0

# leap year or not
if (($lyear%4 == 0) && ($lyear%100 != 100) || ($lyear%400 == 0)) {
 $leap=1; 
}

# arrays of days in a month
my @days31 = qw( 1 3 5 7 8 10 12 );
my @days30 = qw( 4 6 9 11 );
my @days28 = qw( 2 );

# how many days has this month: $lmon
foreach my $days31 (@days31) {
 if (($lmon == $days31) && ($lmday == 31)) { 
  $last_day=1;
 }
}

foreach my $days30 (@days30) {
 if (($lmon == $days30) && ($lmday == 30)) { 
  $last_day=1;
 }
}

foreach my $days28 (@days28) { 
 if ($leap == 0) {
  if (($lmon == $days28) && ($lmday == 28)) { 
   $last_day=1;
  }
 }
 if ($leap == 1) {
  if (($lmon == $days28) && ($lmday == 29)) { 
   $last_day=1;
  }
 }
}
##############################################################################

# if not running for more that 1 year and at the end of the month, 
# day 28(29), 30 or 31
if (($years < 1) && ($phase1 == $lhour) && ($last_day)) { 
    system $COPY_DOM_TYPE4;
}

_MARKER_
close $HVA_C_BODY;

# + execute perms
system "chmod +x hvautoc_*";

return;
}

# is node alive?
sub is_node_down {

    print "is node down not yet implemented\n";
 exit;
}

# list volumes in system
sub list {   
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $porta, $portb, $portc, $user_acc,
    $userdira, $userdirb, $userdirc, $diffconstprefix, $fullprefix) = @_;
 
    print <<EOT;

    This option lists all volumes stored in DIASER. 

    Proceed?

EOT
    
    # select yes to continue or no to exit
 my $response = " ";
 while ($response !~ /yes|y|no|n/ix) {
  print "Please enter (y)yes or (n)no\n";
  $response = <>;
  chomp $response; 
  if ($response =~ /n|no/ix) { exit; }
 }
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    my $user_pass = $diaser_passes[0]; 
   
    # list command
    my $LIST = "ls -lgho -R";

    # list on node A
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$porta");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    my ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    # this changes the string output of the listing into an array
    my @node_a_listing = split('\n',$stdout);
    my @output_a;
    
    # loops through and matches any array element with a volume name prefix
    foreach my $node_a_listing (@node_a_listing) {
        if( $node_a_listing =~ /($fullprefix|$diffconstprefix)/x) {
            # accumulate interesting additions
            push @output_a, "\n", $node_a_listing;
        }
    }

    print "List of all volumes stored on node A\n";
    print @output_a;
    print "\n"; 
    
    # list on node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$portb");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    # this changes the string output of the listing into an array
    my @node_b_listing = split('\n',$stdout);
    my @output_b;
    
    # loops through and matches any array element with a volume name prefix
    foreach my $node_b_listing (@node_b_listing) {
        if( $node_b_listing =~ /($fullprefix|$diffconstprefix)/x) {
            # accumulate interesting additions
            push @output_b, "\n", $node_b_listing;
        }
    }

    print "\nList of all volumes stored on node B\n";
    print @output_b;
    print "\n"; 
    
    # list on node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$portc");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    # this changes the string output of the listing into an array
    my @node_c_listing = split('\n',$stdout);
    my @output_c;
    
    # loops through and matches any array element with a volume name prefix
    foreach my $node_c_listing (@node_c_listing) {
        if( $node_c_listing =~ /($fullprefix|$diffconstprefix)/x) {
            # accumulate interesting additions
            push @output_c, "\n", $node_c_listing;
        }
    }

    print "\nList of all volumes stored on node C\n";
    print @output_c;
    print "\n";

    return;
}

# force change diaser user account password
sub lockout {
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, $port_c, $user_acc,
    $use_sudo, $sudo_account_name_a, $sudo_account_name_b, 
    $sudo_account_name_c) = @_;
   
    print <<EOT;

    This option will lock all of the DIASER user accounts, you will need
    root access to re-enable the accounts.

    Continue?

EOT
    
    # select yes to continue or no to exit
    my $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }

    my $root_pass_node_a;
    my $root_pass_node_c;
    my $root_pass_node_b;
    my $sudo_pass_node_a;
    my $sudo_pass_node_c;
    my $sudo_pass_node_b;

    if ($use_sudo == 0) {
        # pop the root passwords generated by ask_node_pass()
        my @root_passes = ask_node_pass();
        $root_pass_node_c = pop(@root_passes); 
        $root_pass_node_b = pop(@root_passes); 
        $root_pass_node_a = pop(@root_passes); 
    
    } else {
        
        my @sudo_passes = ask_sudo_pass(1);

        $sudo_pass_a = $sudo_passes[0];
        my $crypt_sudo_pass_a = $sudo_passes[1];

        $sudo_pass_b = $sudo_passes[2];
        my $crypt_sudo_pass_b = $sudo_passes[3];

        $sudo_pass_c = $sudo_passes[4];
        my $crypt_sudo_pass_c = $sudo_passes[5];
    }
    
    # -d switch locks all diaser accounts leaving access only for root
    my $DISABLE="/usr/bin/passwd -d ${user_acc}"; # Remote command.
    my $DISABLE_A="echo ${sudo_pass_a} | sudo -S" 
    ." /usr/bin/passwd -d ${user_acc}"; 
    my $DISABLE_B="echo ${sudo_pass_b} | sudo -S" 
    ." /usr/bin/passwd -d ${user_acc}"; 
    my $DISABLE_C="echo ${sudo_pass_c} | sudo -S"
    ." /usr/bin/passwd -d ${user_acc}"; 
    
    # lock diaser user on node A
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$port_a");
 
    # if sudo
    if ($use_sudo == 0) { 
        my ($return) = $ssh->login("root", "$root_pass_node_a");
        $ssh->cmd("$DISABLE");
    } else {
        my ($return) = $ssh->login("$sudo_account_name_a", "$sudo_pass_a");
        $ssh->cmd("$DISABLE_A");
    } 
 
    print "Please change DIASER account password for node A\n";
 
    # lock diaser user on node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$port_b");
    
    # if sudo
    if ($use_sudo == 0) { 
        my ($return) = $ssh->login("root", "$root_pass_node_b");
        $ssh->cmd("$DISABLE");
    } else {
        my ($return) = $ssh->login("$sudo_account_name_b", "$sudo_pass_b");
        $ssh->cmd("$DISABLE_B");
    } 

    print "Please change DIASER account password for node B\n";
 
    # lock diaser user on node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$port_c");
    
    # if sudo
    if ($use_sudo == 0) { 
        my ($return) = $ssh->login("root", "$root_pass_node_c");
        $ssh->cmd("$DISABLE");
    } else {
        my ($return) = $ssh->login("$sudo_account_name_c", "$sudo_pass_c");
        $ssh->cmd("$DISABLE_C");
    } 
 
    print "Please change DIASER account password for node C\n";

    return;
}

# display condensed amount of logs according to user choice
sub logs {   
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $porta, $portb, $portc, $user_acc,
    $userdira, $userdirb, $userdirc, $diffconstprefix, $fullprefix) = @_;
    my $max_lines = 1000;
    my $test = 0;
 
    print <<EOT;

    This option displays a condensed set of logs from each node.

    Proceed?

EOT
    
    # select yes to continue or no to exit
    my $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }  
    
    # how many lines
    while ($test == 0) {
    print "\nHow many lines of log files do you want to read? [${max_lines}]\n";
        # read input
        $max_lines = <>;
        chomp $max_lines; 
        # test if integer
        if(is_integer($max_lines)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    my $user_pass = $diaser_passes[0];

    # read the logs on node A
    my $node="a";
    my $LIST = "cat log_${node}"; 

    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$porta");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    my ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    # parse and print logs from node A
    print "Logs from node A\n\n";
    print log_parser($stdout, $max_lines);
    
    # read the logs on node B
    $node="b";
    $LIST = "cat log_${node}"; 

    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$portb");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    # parse and print logs from node B
    print "\nLogs from node B\n\n";
    print log_parser($stdout, $max_lines);
    
    # read the logs on node C
    $node="c";
    $LIST = "cat log_${node}"; 

    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$portc");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    # parse and print logs from node C
    print "\nLogs from node C\n\n";
    print log_parser($stdout, $max_lines);

    return;
}

sub log_parser {
    
    # unpack scalar parameters
    my ($node_log, $max_lines) = @_;

    # this changes the string output of the log listing into an array
    my @node_listing = split('\n',$node_log);
    
    # reverse the order of @node_a_listing
    @node_listing = reverse @node_listing;

    my $repeat_count=0; # how many times has previous log entry been repeated
    my $repeat_count_prev=0; # how many times has previous log entry been 
    # repeated
    
    my @field_last; # match fields 2-4 for repeat check
    my @field_current; # match fields 2-4 for repeat check
    my $num_lines=0;
    my @output;

    foreach my $node_listing (@node_listing) {

        # iteration count
        $num_lines += 1;
        if ($num_lines == $max_lines) { last; }

        # split each element separated by a space 
        # on each line of log into an array
        @field_current = (split / +/, $node_listing);
        
        # check if the last log entry was the same or very similar if the line
        # of log file was large enough
        if(($field_last[2]) # ensure fields contain something
            && ($field_last[3]) #  
            && (scalar @field_current >= 4) 
            && ($field_current[2] eq $field_last[2])
            && ($field_current[3] eq $field_last[3])) {
                $repeat_count += 1;
                $repeat_count_prev = $repeat_count;
        } else {
            $repeat_count = 0; # reset repeat count
        }

        # information required from rsync and fill_diaser ops
        if ( $repeat_count == 0 ) {
                
            if ($repeat_count_prev !=0) {
                push @output, "\n", " times ", $repeat_count_prev, 
                "last entry repeated ";
                my @last_field = reverse @field_last;
                push @output, "\n", @last_field;
                $repeat_count_prev = 0;
            }
                
            # interesting log entries
                
            if (($node_listing =~ /sent/x)
                || ($node_listing =~ /total size is/x)
                || ($node_listing =~ /Number of files:/x)
                || ($node_listing =~ /diaser_fill/x)) {
                push @output, "\n", $node_listing;
            }
        }   

    @field_last = @field_current;
}

# reverse @output
@output = reverse @output;
return @output;
}

# migrate node to another location
sub migrate {
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, 
    $port_c, $user_acc) = @_;

    print <<EOT;

    >> Partially complete but will work if used with care <<

    migrate will assist you in moving an existing node from the current
    machine, server or workstation, to a new one. This may be located anywhere
    as long as it satisfies the requirements for DIASER inter-node-visibility.
    See docs/manual.

    The procedure may take anywhere from minutes to hours depending on the
    amount of data stored on the existing node and network bandwidth 
    available.

    Proceed?

EOT
 
    # select yes to continue or no to exit
 my $response = " ";
 while ($response !~ /yes|y|no|n/ix) {
  print "Please enter (y)yes or (n)no\n";
  $response = <>;
  chomp $response; 
  if ($response =~ /n|no/ix) { exit; }
 }
    
=pod
migrate could also double up in part as node replicate, this means that
the node identity change, in ROADMAP as automatic node fail-over
    
basic procedure
1)    select a node to migrate
2)    enter ip, ssh port of new node
3)    as root create an account on the new node
4)    re-generate passwordless certs on each node
5)    stat the size of the archives stored within the old node
6)    calculate the time a transfer is likely to take
7)    login as a user to old node account
8)    use rsync to copy the data from old account to new node account
9)    ensure the new node has the correct node identity, i.e. A, B or C
10)   validate that the transfer was error free
11)   delete the old node account and all data
12)   ensure the configuration files know about the new node
=cut

    my $test = 0;
    my $ns_choice;  # node source to migrate
    my $s_node;
    my $s_port;
    my $d_node;
    my $d_port;
    my $new_user_dir;
    
    # which source node
    while ($test == 0) {
        print "Which node do you need to migrate? [A, B or C]\n";
        # read input
        $ns_choice = <>;
        chomp $ns_choice; 
        # test if string and A, B or C
        if ($ns_choice =~ /A|B|C/x) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter A, B or C:\n\n";
        }
    }
    
    # assign source node
    if ($ns_choice eq "A") { 
        $s_node = $node_a;
        $s_port = $port_a;
    }
    if ($ns_choice eq "B") { 
        $s_node = $node_b;
        $s_port = $port_b;
    }
    if ($ns_choice eq "C") { 
        $s_node = $node_c;
        $s_port = $port_c;
    }

    # reset test
    $test = 0;
    
    # request new destination node IP address
    while ($test == 0) {
        print "\nWhat is the IP address of the new node?" 
        ." [${s_node}]\n";
        # read input
        $d_node = <>;
        chomp $d_node; 
        # test if IP address
        if(is_ip($d_node)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an IP address:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # request port of ssh on new destination node
    while ($test == 0) {
        print "\nWhat port will the new node use for SSH? [22]\n";
        # read input
        $d_port = <>;
        chomp $d_port; 
        # test if integer
        if(is_integer($d_port)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # new node root password
    my $new_node_pass1 = "1"; 
    my $new_node_pass2 = "2";
    while ($new_node_pass1 ne $new_node_pass2) {
        print "Please enter the root password for new node\n";
        ReadMode('noecho');
        $new_node_pass1 = <>;
        chomp $new_node_pass1; 
        print "Please enter the password again\n";
        $new_node_pass2 = <>;
        chomp $new_node_pass2; 
        ReadMode('normal');
        if ($new_node_pass1 ne $new_node_pass2) { 
        print "The passwords are not the same, try again\n";
        }  
    }
    my $new_root_pass = $new_node_pass2; 
    print "Thank you\n";
    print "\n";
    # reset test
    $test = 0;
    
    # the directory on new node to create the diaser user account
    while ($test == 0) {
        print "\nWhat is the root directory where DIASER will create the\n" 
        ."new encasing user account on node? [/home/diaser/]\n";
        # read input
        $new_user_dir = <>;
        chomp $new_user_dir; 
        # test if directory
        if(is_directory($new_user_dir)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter the full directory pathway"
            ." including leading and trailing /s:\n\n";
        }
    }
    # reset test
    $test = 0;
   
    # plain-text and crypt password generated by ask_diaser_pass()
    # also ask for a password check by using parameter scalar 1
    my @diaser_passes = ask_diaser_pass(1);
    $user_pass = $diaser_passes[0]; 
    my $crypt_user_pass = $diaser_passes[1]; 

    my $ADD_USER_NEW="/usr/sbin/useradd -g users -G" 
    ." sshd -d ${new_user_dir}${user_acc}" 
    ." -m -p ${crypt_user_pass} ${user_acc}";
    
    # setup diaser user on new node
    my $ssh = Net::SSH::Perl->new("$d_node", debug => "0", port => "$d_port");
    my ($return) = $ssh->login("root", "$new_root_pass");
    $ssh->cmd("$ADD_USER_NEW");
    print "User ${user_acc} added to node A\n";

    # set up certs between nodes again
    passwordless_login($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
 
    # command to display a dry run
    my $TRANSFER_DATA_DRY = "rsync -az -e 'ssh -p ${d_port}' --numeric-ids" 
    ." --stats --ignore-errors --dry-run ~/ ${user_acc}\@${d_node}:";
 
    $ssh = Net::SSH::Perl->new("$s_node", debug => "0", port => "$s_port");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    #$ssh->cmd("touch wasere");
    my ($stdout,$stderr,$exit) = $ssh->cmd("$TRANSFER_DATA_DRY");
    print $stdout; # loop round this to have continuous output until undef?
    
    # while ($stdout) {
    # print $stdout;
    # sleep 5
    # }
    
    print $stderr;

    # select yes to continue or no to exit
    $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
    
    # command to transfer from on source to destination node
    my $TRANSFER_DATA = "rsync -azv -e 'ssh -p ${d_port}' --numeric-ids" 
    ." --stats --ignore-errors ~/ ${user_acc}\@${d_node}:";
 
    $ssh = Net::SSH::Perl->new("$s_node", debug => "0", port => "$s_port");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$TRANSFER_DATA");
    print $stdout; # loop round this to have continuous output until undef?
    
    #  not sure if this loop works yet
    #while ($stdout) {
    #    print $stdout;
    #    sleep 5;
    #}

    # now run upgrade to re-set the node identities
    modify();
    
    # test that the transfer of data is valid, does the size of data from the
    # source node match that of the destination node?
    
    # >> coded check here
    print "a coded check here\n\n";

    print <<EOT;

    Now we need to remove the data from your chosen source node at 
    ${s_node}, this action is permanent and cannot be undone.
    
    Proceed?

EOT
    
    # select yes to continue or no to exit
    $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
    
    # select yes to continue or no to exit, check a second time
    $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Are you sure? (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
    
    my $DEL_USER="/usr/sbin/userdel -r ${user_acc}"; 

    # remove diaser user from node A
     $ssh = Net::SSH::Perl->new("$s_node", debug => "0", port => "$s_port");
    ($return) = $ssh->login("root", "$new_root_pass");
    $ssh->cmd("$DEL_USER");
    #print $return;
    #print "${s_node} clear\n";

    exit;
}

# modify settings or parameters
sub modify {   
    
    print <<EOT;

    This will apply your modified settings, generate and send a new RSA 
    certificate to the nodes.

    Continue?

EOT
    # select yes to continue or no to exit
    my $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
 
    print "Please wait, this may take a few minutes...\n";
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    $user_pass = $diaser_passes[0]; 
 
    passwordless_login($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
 
    gen_hvautoc($config->NUM_YEARS, $config->START_YEAR, $config->HOUR1, 
    $config->HOUR2, $config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C,
    $config->DRY_RUN, $config->LOW_MAX_BW, 
    $config->TZONE_A, $config->TZONE_B, $config->TZONE_C, 
    $config->USER_ACC, $config->TOUT,
    $config->NUM_MONTHS, $config->START_DOM, $config->START_MONTH,   
    $config->COLLECT_FULL_DAY); 
 
    send_hvautoc_algol($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
 
    gen_fill($config->TZONE_A,
    $config->USER_ACC, $config->DIR_A, $config->FILL_START_TIME, 
    $config->VOLUME_DIR, $config->DIFF_CONST_PREFIX, $config->COLLECT_FULL, 
    $config->COLLECT_FULL_DAY, $config->FULL_PREFIX); 
    
    send_fill($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
    
    send_cron_defs($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);

    return;
}

# create passwordless login between nodes
sub passwordless_login { 
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, $port_c, $user_acc) = @_;
 
    my $PERMS="chown -R ${user_acc}.users .ssh && chmod 600"
    ." .ssh/authorized_keys";
    my $MAKE_SSH="mkdir .ssh";
 
    # generate RSA keys for passwordless login locally first
    # '' means no password is used in combination with the keys, 
    # created, used then deleted
    system "ssh-keygen -t rsa -f id_rsa -N ''"; 
    # will use id_rsa and id_rsa.pub
 
    # create a local authorised key file
    system "cat id_rsa.pub > authorized_keys"; 
 
    # make .ssh on node A
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$port_a");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("$MAKE_SSH");
    print ".ssh created A\n";
 
    # make .ssh on node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$port_b");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("$MAKE_SSH");
    print ".ssh created B\n";
 
    # make .ssh on node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$port_c");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("$MAKE_SSH");
    print ".ssh created C\n";
 
    # transfer files to node A
    my $sftp = Net::SFTP->new("$node_a",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_a]) or die "Could not create SFTP connection";
    # send files
    $sftp->put("id_rsa",".ssh/id_rsa"); 
    $sftp->put("authorized_keys",".ssh/authorized_keys"); 
    print "certs transferred to A\n";
 
    # transfer files to node B
    $sftp = Net::SFTP->new("$node_b",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_b]) or die "Could not create SFTP connection";
    # send files
    $sftp->put("id_rsa",".ssh/id_rsa"); 
    $sftp->put("authorized_keys",".ssh/authorized_keys"); 
    print "certs transferred to B\n";
 
    # transfer files to node C
    $sftp = Net::SFTP->new("$node_c",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_c]) or die "Could not create SFTP connection";
    # send files
    $sftp->put("id_rsa",".ssh/id_rsa"); 
    $sftp->put("authorized_keys",".ssh/authorized_keys"); 
    print "certs transferred to C\n";
 
    # chown/chmod .ssh on node A
    $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$port_a");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("$PERMS");
    print "permissions set on node A\n";
 
    # chown/chmod .ssh on node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$port_b");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("$PERMS");
    print "permissions set on node B\n";
 
    # chown/chmod .ssh on node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$port_c");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("$PERMS");
    print "permissions set on node C\n";
    system "rm authorized_keys id_rsa id_rsa.pub"; 

    return;
}

# pause operation and all running copy ops
sub pause {   
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, 
    $port_c, $user_acc) = @_;
    
    print <<EOT;

    This option will pause DIASER copies currently in operation, until
    the resume options is used.

    Continue?

EOT
    # select yes to continue or no to exit
    my $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    my $user_pass = $diaser_passes[0];
    
    # node A
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$port_a");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    my ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_a_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_a_listing (@node_a_listing) {
        if( $node_a_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_a_listing) [0];
            # command to killall rsync processes
            my $PAUSE = "kill -STOP ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$PAUSE");
            print "node A process id ${pid} paused\n";
        }
    }
    
    # node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$port_b");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_b_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_b_listing (@node_a_listing) {
        if( $node_b_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_b_listing) [0];
            # command to killall rsync processes
            my $PAUSE = "kill -STOP ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$PAUSE");
            print "node B process id ${pid} paused\n";
        }
    }
    
    # node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$port_c");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_c_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_c_listing (@node_c_listing) {
        if( $node_c_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_c_listing) [0];
            # command to killall rsync processes
            my $PAUSE = "kill -STOP ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$PAUSE");
            print "node C process id ${pid} paused\n";
        }
    }
    exit;
}

# remove unwanted or expired archives
sub prune {   
 
    print "prune not yet implemented\n";
    exit;
}

# migrate node to another location
sub recreate {
    
    # unpack scalar parameters
    my ($num_years, $start_year, $node_a, $node_b, $node_c, $port_a, 
    $port_b, $port_c, $user_acc, $userdira, $userdirb, $userdirc) = @_;
    
    print <<EOT;

    recreate will re-establish a particular node on a particular
    machine. This will generate a user account, directories and certificates
    between nodes. No archive data us copied or re-built from another node.

    Proceed?

EOT
 
    # select yes to continue or no to exit
    my $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
    
    my $test = 0;
    my $node;
    my $port;
    my $user_dir;
    my $n_choice;
    my $new_user_dir;
    
    # which node to recreate?
    while ($test == 0) {
        print "Which node would you like to recreate? [A, B or C]\n";
        # read input
        $n_choice = <>;
        chomp $n_choice; 
        # test if string and A, B or C
        if ($n_choice =~ /A|B|C/x) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter A, B or C:\n\n";
        }
    }
    
    # assign node
    if ($n_choice eq "A") { 
        $node = $node_a;
        $port = $port_a;
        $new_user_dir = $userdira;
    }
    if ($n_choice eq "B") { 
        $node = $node_b;
        $port = $port_b;
        $new_user_dir = $userdirb;
    }
    if ($n_choice eq "C") { 
        $node = $node_c;
        $port = $port_c;
        $new_user_dir = $userdirc;
    }

    # new node root password
    my $new_node_pass1 = "1"; 
    my $new_node_pass2 = "2";
    while ($new_node_pass1 ne $new_node_pass2) {
        print "Please enter the root password for new node\n";
        ReadMode('noecho');
        $new_node_pass1 = <>;
        chomp $new_node_pass1; 
        print "Please enter the password again\n";
        $new_node_pass2 = <>;
        chomp $new_node_pass2; 
        ReadMode('normal');
        if ($new_node_pass1 ne $new_node_pass2) { 
        print "The passwords are not the same, try again\n";
        } 
    }
    my $new_root_pass = $new_node_pass2; 
    print "Thank you\n";
    print "\n";
   
    # plain-text and crypt password generated by ask_diaser_pass()
    # also ask for a password check by using parameter scalar 1
    my @diaser_passes = ask_diaser_pass(1);
    $user_pass = $diaser_passes[0]; 
    my $crypt_user_pass = $diaser_passes[1]; 

    my $ADD_USER_NEW="/usr/sbin/useradd -g users -G" 
    ." sshd -d ${new_user_dir}${user_acc}" 
    ." -m -p ${crypt_user_pass} ${user_acc}";
    
    # setup diaser user on new node
    my $ssh = Net::SSH::Perl->new("$node", debug => "0", port => "$port");
    my ($return) = $ssh->login("root", "$new_root_pass");
    $ssh->cmd("$ADD_USER_NEW");
    print "User ${user_acc} added to node A\n";

    # setup diaser slots on node
    
    # year calculation using start year defined in diaser.conf
    # touch log at ~/ when making years
    my $year = $start_year;
    my $MAKE_YEARS="mkdir ${year}"; 
    
    # start in January, may as well, operation start date may differ
    my $month=1; 
 
    # month directory definitions
    my $MAKE_MONTHS="cd ${year} && mkdir mth1 mth2 mth3 mth4 mth5 mth6 mth7" 
    ." mth8 mth9 mth10 mth11 mth12"; 
 
    # each occupies a month, max 31 days covers variable month length, 
    # d stands for day
    my $MAKE_DAYS="cd ${year} && cd mth${month} && mkdir Full01 Full02" 
    ." d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19"
    ." d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31";
 
    # loop through n*chosen years, create years then populate with months 
    while ($num_years > 0) {
       
        # setup dirs on new node
        my $ssh = Net::SSH::Perl->new("$node", debug => "0", 
        port => "$port");
         my ($return) = $ssh->login("$user_acc", "$user_pass");
        $ssh->cmd("$MAKE_YEARS");
        $ssh->cmd("$MAKE_MONTHS");
        print "Year ${year} and months created on node ${n_choice}\n";
        
        # populate the months with day slots 
        print "Populating ${year} months with day slots...\n";
        # 12 iterations for each node per year
            while ($month < 13) {
                my $ssh = Net::SSH::Perl->new("$node", debug => "0", 
                port => "$port");
                my ($return) = $ssh->login("$user_acc", "$user_pass");
                $ssh->cmd("$MAKE_DAYS");
   
                $month += 1; # increment the month

                # re-declare $MAKE_DAYS
                $MAKE_DAYS="cd ${year} && cd mth${month} && mkdir Full01" 
                ." Full02 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15"                ." d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30"
                ." d31";
        
            }
        print "Complete...\n";
  
        $year += 1; # increment the year
        $month = 1; # reset month
 
        # re-declare $MAKE_YEARS, $MAKE_MONTHS and $MAKE_DAYS
        $MAKE_YEARS="mkdir ${year}"; 
 
        $MAKE_MONTHS="cd ${year} && mkdir mth1 mth2 mth3 mth4 mth5 mth6" 
        ." mth7 mth8 mth9 mth10 mth11 mth12"; 
 
        $MAKE_DAYS="cd ${year} && cd mth${month} && mkdir Full01 Full02" 
        ." d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18"
        ." d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31"; 
        
        $num_years -= 1; # decrement the year count 
    }

    # set up certs between nodes again
    passwordless_login($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);

    # now run upgrade to re-set the node identities
    modify();

    exit;
}

# delete diaser accounts, remove system
sub remove { 
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, $port_c, $user_acc,
    $use_sudo, $sudo_account_name_a, $sudo_account_name_b, 
    $sudo_account_name_c) = @_;
   
    my $root_pass_node_a;
    my $root_pass_node_c;
    my $root_pass_node_b;
    my $sudo_pass_node_a;
    my $sudo_pass_node_c;
    my $sudo_pass_node_b;

    if ($use_sudo == 0) {
        # pop the root passwords generated by ask_node_pass()
        my @root_passes = ask_node_pass();
        $root_pass_node_c = pop(@root_passes); 
        $root_pass_node_b = pop(@root_passes); 
        $root_pass_node_a = pop(@root_passes); 
    
    } else {
        
        my @sudo_passes = ask_sudo_pass(1);

        $sudo_pass_a = $sudo_passes[0];
        my $crypt_sudo_pass_a = $sudo_passes[1];

        $sudo_pass_b = $sudo_passes[2];
        my $crypt_sudo_pass_b = $sudo_passes[3];

        $sudo_pass_c = $sudo_passes[4];
        my $crypt_sudo_pass_c = $sudo_passes[5];
    }

    my $DEL_USER="/usr/sbin/userdel -r ${user_acc}"; # Remote command.
    my $DEL_USER_A="echo ${sudo_pass_a} | sudo -S" 
    ." /usr/sbin/userdel -r ${user_acc}"; 
    my $DEL_USER_B="echo ${sudo_pass_b} | sudo -S" 
    ." /usr/sbin/userdel -r ${user_acc}"; 
    my $DEL_USER_C="echo ${sudo_pass_c} | sudo -S"
    ." /usr/sbin/userdel -r ${user_acc}"; 
    
    # remove diaser user from node A
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$port_a");
 
    # if sudo
    if ($use_sudo == 0) { 
        my ($return) = $ssh->login("root", "$root_pass_node_a");
        $ssh->cmd("$DEL_USER");
    } else {
        my ($return) = $ssh->login("$sudo_account_name_a", "$sudo_pass_a");
        $ssh->cmd("$DEL_USER_A");
    } 
 
    #print $return;
    print "Node A clear\n";
 
    # remove diaser user from node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$port_b");
    
    # if sudo
    if ($use_sudo == 0) { 
        my ($return) = $ssh->login("root", "$root_pass_node_b");
        $ssh->cmd("$DEL_USER");
    } else {
        my ($return) = $ssh->login("$sudo_account_name_b", "$sudo_pass_b");
        $ssh->cmd("$DEL_USER_B");
    } 

    #print $return;
    print "Node B clear\n";
 
    # remove diaser user from node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$port_c");
    
    # if sudo
    if ($use_sudo == 0) { 
        my ($return) = $ssh->login("root", "$root_pass_node_c");
        $ssh->cmd("$DEL_USER");
    } else {
        my ($return) = $ssh->login("$sudo_account_name_c", "$sudo_pass_c");
        $ssh->cmd("$DEL_USER_C");
    } 
 
    #print $return;
    print "Node C clear - DIASER completely removed\n";
    
    return;
}

# report, data storage stats
sub report {
 
    print "report not yet implemented\n";
    exit;
}

# reset diaser account passwords for security
sub reset_passwd {
 
    print "reset not yet implemented\n";
    exit;
}

# resume resume paused copies, allow scheduled copies if stopped
sub resume {
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, 
    $port_c, $user_acc) = @_;
    
    print <<EOT;

    This option will resume DIASER copies currently in operation.

    Continue?

EOT
    # select yes to continue or no to exit
    my $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    my $user_pass = $diaser_passes[0];
    
    # node A
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$port_a");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    my ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_a_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_a_listing (@node_a_listing) {
        if( $node_a_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_a_listing) [0];
            # command to killall rsync processes
            my $RESUME = "kill -CONT ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$RESUME");
            print "node A process id ${pid} resumed\n";
        }
    }
    
    # node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$port_b");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_b_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_b_listing (@node_a_listing) {
        if( $node_b_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_b_listing) [0];
            # command to killall rsync processes
            my $RESUME = "kill -CONT ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$RESUME");
            print "node B process id ${pid} resumed\n";
        }
    }
    
    # node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$port_c");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_c_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_c_listing (@node_c_listing) {
        if( $node_c_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_c_listing) [0];
            # command to killall rsync processes
            my $RESUME = "kill -CONT ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$RESUME");
            print "node C process id ${pid} resumed\n";
        }
    }
    exit;
}

# retrieve stored data
sub retrieve {
    
    # unpack scalar parameters
    my ($nodea, $nodeb, $nodec, $porta, $portb, $portc, $user_acc,
    $r_year, $r_month, $r_day, $r_full) = @_;

    my $test = 0;
    my $n_choice;
    my $fc_choice;
    my $node;
    my $port;
    my $day;
    my $file;

    # introduction
    print "\nThis tool will guide you through retrieving archive data\n";
    print "from DIASER. Please answer the questions...\n\n";
    
    # which node
    while ($test == 0) {
        print "Which node would you like to retrieve from? [A, B or C]\n";
        # read input
        $n_choice = <>;
        chomp $n_choice; 
        # test if string and A, B or C
        if ($n_choice =~ /A|B|C/x) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter A, B or C:\n\n";
        }
    }
    
    # assign node
    if ($n_choice eq "A") { 
        $node = $nodea;
        $port = $porta;
    }
    if ($n_choice eq "B") { 
        $node = $nodeb;
        $port = $portb;
    }
    if ($n_choice eq "C") { 
        $node = $nodec;
        $port = $portc;
    }

    # reset test
    $test = 0;
    
    # which year
    while ($test == 0) {
        print "Which year would you like to retrieve from? [${r_year}]\n";
        # read input
        $r_year = <>;
        chomp $r_year; 
        # test if integer
        if(is_integer($r_year)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    # which month
    while ($test == 0) {
        print "Which month would you like to retrieve from? [${r_month}]\n";
        # read input
        $r_month = <>;
        chomp $r_month; 
        # test if integer
        if(is_integer($r_month)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an integer greater than 0:\n\n";
        }
    }
    # reset test
    $test = 0;
   
    # full or differential or constant
    while ($test == 0) {
        print "Would you like to retrieve a Full or Diff /"
        ." Constant volume [F or D]\n";
        # read input
        $fc_choice = <>;
        chomp $fc_choice; 
        # test if integer
        if($fc_choice =~ /F|D/x) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter F or D:\n\n";
        }
    }
    # reset test
    $test = 0;
    
    if ($fc_choice eq "F") {
        $day = $r_full;        
    } else {
    
        # which day
        while ($test == 0) {
            print "Which day would you like to retrieve from? [${r_day}]\n";
            # read input
            $r_day = <>;
            chomp $r_day; 
            # test if integer
            if(is_integer($r_day)) { 
                $test = 1;
                $day = $r_day;
                $day = "d$day";
            }
            else {
                print "Incorrect - please enter an integer greater than 0:\n\n";
            }
        }
    }
    # reset test
    $test = 0;
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    $user_pass = $diaser_passes[0]; 
    
    # list files - until then use glob below    
    my $sftp = Net::SFTP->new("$node",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port]) or die "Could not create SFTP connection";
    # get remote files from specified directory
    $sftp->ls("${r_year}/mth${r_month}/${day}/", 
    sub { print $_[0]->{longname}, "\n\n" }); 
    
    # which file
    while ($test == 0) {
        print "Which file from the list above would you like to retrieve"
        ." ?\n";
        # read input
        $file = <>;
        chomp $file; 
        # test if string
        if(is_string($file)) { 
            $test = 1;
        }
        else {
            print "Incorrect - please enter an alpha numeric string:\n\n";
        }
    }

    # retrieve file(s)
    $sftp = Net::SFTP->new("$node",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port]) or die "Could not create SFTP connection";
    # get remote files from specified directory
    $sftp->get("${r_year}/mth${r_month}/${day}/${file}","${file}"); 
    print "Thank you\n";

    exit;
}

# send nodes crontab definitions
sub send_cron_defs { 
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, $port_c, $user_acc) = @_;

# generate tab_b.pl and tab_c.pl, tab_a.pl is created by sub gen_hvaotoc
open my $GEN_TAB_B, '>', 'tab_b.pl'
    or croak "Can't create tab_b.pl: $OS_ERROR";
print $GEN_TAB_B <<"_MARKER_";
0 * * * * ~/hvautoc_b.pl
   
_MARKER_
close $GEN_TAB_B;

open my $GEN_TAB_C, '>', 'tab_c.pl'
    or croak "Can't create tab_c.pl: $OS_ERROR";
print $GEN_TAB_C <<"_MARKER_";
0 * * * * ~/hvautoc_c.pl
   
_MARKER_
close $GEN_TAB_C;

# update permissions 
system "chmod +x tab_b.pl";
system "chmod +x tab_c.pl";

    # transfer files, cron tabs to node A
    my $sftp = Net::SFTP->new("$node_a",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_a]) or die "Could not create SFTP connection";
    # Send files.
    $sftp->put("tab_a.pl","tab_a.pl"); 
    print "cron tab templates transferred to A\n";
 
    # transfer files, cron tabs to node B
    $sftp = Net::SFTP->new("$node_b",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_b]) or die "Could not create SFTP connection";
    # send files
    $sftp->put("tab_b.pl","tab_b.pl"); 
    print "cron tab templates transferred to B\n";
 
    # transfer files, cron tabs to node C
    $sftp = Net::SFTP->new("$node_c",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_c]) or die "Could not create SFTP connection";
    # send files
    $sftp->put("tab_c.pl","tab_c.pl"); 
    print "cron tab templates transferred to C\n";
 
    # still need to run the cron scripts, set up cron jobs on nodes
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$port_a");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("crontab tab_a.pl");
    #print $return;
    print "crontabs setup on node A\n";
 
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$port_b");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("crontab tab_b.pl");
    #print $return;
    print "crontabs setup on node B\n";
 
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$port_c");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    $ssh->cmd("crontab tab_c.pl");
    #print $return;
    print "crontabs setup on node C\n";

    return;
}

# send node a fill script
sub send_fill { 
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, $port_c, $user_acc) = @_;
  
    # transfer file fill_diaser.pl to node A
    my $sftp = Net::SFTP->new("$node_a",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_a]) or die "Could not create SFTP connection";
    # Send files.
    $sftp->put("fill_diaser.pl","fill_diaser.pl"); 
    print "fill_diaser.pl transferred to A\n";

    return;
} 

# send nodes hvautoc algorithm
sub send_hvautoc_algol { 
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, $port_c, $user_acc) = @_;
 
    # transfer files, cron tabs to node A
    my $sftp = Net::SFTP->new("$node_a",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_a]) or die "Could not create SFTP connection";
    # Send files.
    $sftp->put("hvautoc_a.pl","hvautoc_a.pl"); 
    print "hvautoc algorithm transferred to A\n";
 
    # transfer files, cron tabs to node B
    $sftp = Net::SFTP->new("$node_b",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_b]) or die "Could not create SFTP connection";
    # send files
    $sftp->put("hvautoc_b.pl","hvautoc_b.pl"); 
    print "hvautoc algorithm transferred to B\n";
 
    # transfer files, cron tabs to node C
    $sftp = Net::SFTP->new("$node_c",
    user=>$user_acc, 
    password=>$user_pass,
    ssh_args => [port => $port_c]) or die "Could not create SFTP connection";
    # send files
    $sftp->put("hvautoc_c.pl","hvautoc_c.pl"); 
    print "hvautoc algorithm transferred to C\n";

    return;
} 

# stats and run-time information
sub stats {   
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $porta, $portb, $portc, $user_acc,
    $userdira, $userdirb, $userdirc, $diffconstprefix, $fullprefix) = @_;
 
    print <<EOT;

    This option displays for each node in GiB; disk space, total daily volumes, 
    total full volumes, total data stored on each node and average
    differential volume size.

    Proceed?

EOT
    
    # select yes to continue or no to exit
    my $response = "";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
    
=pod
outline of stats required and how they will collected and presented

stats required
==============
1) disk space used and available
2) volume names, size, date (location)

collection methods
==================
some processing
stdout to display
use stdout redirection to file
ensure correct columns

stats presentation
==================
plot with GNUPLot
perl HTML generation 
=cut

    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    my $user_pass = $diaser_passes[0]; 
   
    # strip trailing / for $userdir* as not used by grep
    # lenght of userdir*
    #node a
    my $length_userdira = length ($userdira);
    $length_userdira -= 1;
    my $short_userdira = substr($userdira, 0, $length_userdira);
    
    #node b
    my $length_userdirb = length ($userdirb);
    $length_userdirb -= 1;
    my $short_userdirb = substr($userdirb, 0, $length_userdirb);
    
    # node c
    my $length_userdirc = length ($userdirc);
    $length_userdirc -= 1;
    my $short_userdirc = substr($userdirc, 0, $length_userdirc);

    # my $DISK_FREE = "df"; 
    #my $DISK_FREE_A = "df -h|grep ${short_userdira}"; 
    #my $DISK_FREE_B = "df -h|grep ${short_userdirb}"; 
    #my $DISK_FREE_C = "df -h|grep ${short_userdirc}"; 
    my $DISK_FREE_A = "df -h"; 
    my $DISK_FREE_B = "df -h"; 
    my $DISK_FREE_C = "df -h"; 
    
    # disk usage
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$porta");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    my ($stdout,$stderr,$exit) = $ssh->cmd("$DISK_FREE_A");

    print "Disk usage node A\n\n";

    print $stdout;
    print "\n"; 
    
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$portb");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$DISK_FREE_B");

    print "Disk usage node B\n\n";

    print $stdout; 
    print "\n"; 
    
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$portc");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$DISK_FREE_C");

    print "Disk usage node C\n\n";

    print $stdout; 
    print "\n"; 
    
    # list recursively through the node directory
    my $LIST = "ls -R -lt"; 
    
    # node A
    $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$porta");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    my $total_full_size_a=0;      # add all full volume sizes 
    my $total_full_size_a_gib=0;  # add all full volume sizes in GiB
    my $total_diff_size_a=0;      # add all diff volume sizes 
    my $total_diff_size_a_gib=0;  # add all diff volume sizes in GiB
    my $num_diffs=0;  # number of differential volumes in system

    # this changes the string output of the listing into an array
    my @node_a_listing = split('\n',$stdout);
    
    # for full volumes
    # loops through and matches any array element with a volume name prefix
    foreach my $node_a_listing (@node_a_listing) {
        if( $node_a_listing =~ /$fullprefix/x) {
        
        # split on white-space of
        # any length and add to $size
        my $size = (split / +/, $node_a_listing) [4]; 
        $total_full_size_a += $size;
        
        }
    }

    # convert to GiB and print
    $total_full_size_a_gib = ($total_full_size_a/1000000000);
    print "Total size (GiB) of full volumes in storage on node A: ";
    print "$total_full_size_a_gib\n";
    
    # for differentials
    # loops through and matches any array element with a volume name prefix
    foreach my $node_a_listing (@node_a_listing) {
        if( $node_a_listing =~ /$diffconstprefix/x) {
        $num_diffs += 1;

        # split on white-space of
        # any length and add to $size
        my $size = (split / +/, $node_a_listing) [4]; 
        $total_diff_size_a += $size;
        
        }
    }

    # convert to GiB and print
    $total_diff_size_a_gib = ($total_diff_size_a/1000000000);
    print "Total size (GiB) of daily volumes in storage on node A: ";
    print "$total_diff_size_a_gib\n";
    print "Total data stored (GiB) on node A: ";
    my $total_a = $total_diff_size_a_gib + $total_full_size_a_gib;
    print "$total_a\n";
    print "Average differential volume size in (GiB) stored on node A: ";
    my $ave_diff_size = $total_diff_size_a_gib / $num_diffs;
    print "$ave_diff_size\n\n";

    # node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$portb");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    my $total_full_size_b=0;      # add all full volume sizes 
    my $total_full_size_b_gib=0;  # add all full volume sizes in GiB
    my $total_diff_size_b=0;      # add all diff volume sizes 
    my $total_diff_size_b_gib=0;  # add all diff volume sizes in GiB

    # this changes the string output of the listing into an array
    my @node_b_listing = split('\n',$stdout);
    
    # for full volumes
    # loops through and matches any array element with a volume name prefix
    foreach my $node_b_listing (@node_b_listing) {
        if( $node_b_listing =~ /$fullprefix/x) {
        
        # split on white-space of
        # any length and add to $size
        my $size = (split / +/, $node_b_listing) [4]; 
        $total_full_size_b += $size;
        
        }
    }

    # convert to GiB and print
    $total_full_size_b_gib = ($total_full_size_b/1000000000);
    print "Total size (GiB) of full volumes in storage on node B: ";
    print "$total_full_size_b_gib\n";
    
    # for differentials
    # loops through and matches any array element with a volume name prefix
    foreach my $node_b_listing (@node_b_listing) {
        if( $node_b_listing =~ /$diffconstprefix/x) {
        
        # split on white-space of
        # any length and add to $size
        my $size = (split / +/, $node_b_listing) [4]; 
        $total_diff_size_b += $size;
        
        }
    }

    # convert to GiB and print
    $total_diff_size_b_gib = ($total_diff_size_b/1000000000);
    print "Total size (GiB) of daily volumes in storage on node B: ";
    print "$total_diff_size_b_gib\n";
    print "Total data stored (GiB) on node B: ";
    my $total_b = $total_diff_size_b_gib + $total_full_size_b_gib;
    print "$total_b\n\n";

    # node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$portc");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("$LIST");
    
    my $total_full_size_c=0;      # add all full volume sizes 
    my $total_full_size_c_gib=0;  # add all full volume sizes in GiB
    my $total_diff_size_c=0;      # add all diff volume sizes 
    my $total_diff_size_c_gib=0;  # add all diff volume sizes in GiB

    # this changes the string output of the listing into an array
    my @node_c_listing = split('\n',$stdout);
    
    # for full volumes
    # loops through and matches any array element with a volume name prefix
    foreach my $node_c_listing (@node_c_listing) {
        if( $node_c_listing =~ /$fullprefix/x) {
        
        # split on white-space of
        # any length and add to $size
        my $size = (split / +/, $node_c_listing) [4]; 
        $total_full_size_c += $size;
        
        }
    }

    # convert to GiB and print
    $total_full_size_c_gib = ($total_full_size_c/1000000000);
    print "Total size (GiB) of full volumes in storage on node C: ";
    print "$total_full_size_c_gib\n";
    
    # for differentials
    # loops through and matches any array element with a volume name prefix
    foreach my $node_c_listing (@node_c_listing) {
        if( $node_c_listing =~ /$diffconstprefix/x) {
        
        # split on white-space of
        # any length and add to $size
        my $size = (split / +/, $node_c_listing) [4]; 
        $total_diff_size_c += $size;
        
        }
    }

    # convert to GiB and print
    $total_diff_size_c_gib = ($total_diff_size_c/1000000000);
    print "Total size (GiB) of daily volumes in storage on node C: ";
    print "$total_diff_size_c_gib\n";
    print "Total data stored (GiB) on node C: ";
    my $total_c = $total_diff_size_c_gib + $total_full_size_c_gib;
    print "$total_c\n\n";

 exit;
}

# stop stop running copy ops
sub stop {   
    
    # unpack scalar parameters
    my ($node_a, $node_b, $node_c, $port_a, $port_b, 
    $port_c, $user_acc) = @_;
    
    print <<EOT;

    This option will stop DIASER copies currently in operation, until
    the next set of transfer operations are initiated. This will kill
    any rsync processes.

    Continue?

EOT
    # select yes to continue or no to exit
    my $response = "";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    my $user_pass = $diaser_passes[0];
    
    # node A
    my $ssh = Net::SSH::Perl->new("$node_a", debug => "0", port => "$port_a");
    my ($return) = $ssh->login("$user_acc", "$user_pass");
    my ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_a_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_a_listing (@node_a_listing) {
        if( $node_a_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_a_listing) [0];
            # command to killall rsync processes
            my $KILL = "kill -9 ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$KILL");
            print "node A process id ${pid} killed\n";
        }
    }
    
    # node B
    $ssh = Net::SSH::Perl->new("$node_b", debug => "0", port => "$port_b");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_b_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_b_listing (@node_a_listing) {
        if( $node_b_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_b_listing) [0];
            # command to killall rsync processes
            my $KILL = "kill -9 ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$KILL");
            print "node B process id ${pid} killed\n";
        }
    }
    
    # node C
    $ssh = Net::SSH::Perl->new("$node_c", debug => "0", port => "$port_c");
    ($return) = $ssh->login("$user_acc", "$user_pass");
    ($stdout,$stderr,$exit) = $ssh->cmd("ps -e");

    # this changes the string output of the listing into an array
    my @node_c_listing = split('\n',$stdout);
    
    # loops through and matches any array element we want to pause
    foreach my $node_c_listing (@node_c_listing) {
        if( $node_c_listing =~ /rsync/) {
            # use this pid
            my $pid = (split / +/, $node_c_listing) [0];
            # command to killall rsync processes
            my $KILL = "kill -9 ${pid}"; 
            ($stdout,$stderr,$exit) = $ssh->cmd("$KILL");
            print "node C process id ${pid} killed\n";
        }
    }
 
    exit;
}

# tzone lookup table, take an input value and return an offset
sub tzone {   
 
    print "stop not yet implemented\n";
    exit;
}

# usage and help for command line options
sub usage {
 
    print "Unknown option: @_\n" if ( @_ );
    print <<EOT;
        
 ${name}_${version} Usage: diaser 
 
 --help                 help|-?

 --bandwidth            calculate real bandwidth throughput between nodeX-Y
 --configure            question driven configuration tool
 --extend               extend maximum storage structure date 
 --install              install
 --list                 list all volumes in storage
 --lock                 lock all DIASER node accounts
 --logs                 condensed log readings from nodes
 --migrate              migrate node 
 --modify   [opts]      send modified configuration to nodes either from 
                        conf file or command options or both
 --pause                pause operation 
 --recreate             recreate a single node from scratch
 --remove               remove from nodes, all data will be lost 
 --resume               resume operation 
 --retrieve [opts]      retrieve archive data 
 --stats                generate statistics 
 --stop                 stop operation 
 --upgrade              apply upgrades   
 --version              show version  
 
 For more information please use man diaser.

 Please send any FEEDBACK to dlb\@interlinux.org.uk. I'm especially 
 interested in how DIASER may be of use to you now or in the future.
 Thank you.

EOT
   exit;
}

# upgrade DIASER
sub upgrade {   
 
print <<EOT;
 
Upgrade to ${name}_${version}.

    This will upgrade passwordless logins between nodes, 
    the node hypervirtual autochangers and cron tabs.
    
    Also generate and send a new RSA certificate to your nodes.

    Continue?

EOT
    # select yes to continue or no to exit
    my $response = " ";
    while ($response !~ /yes|y|no|n/ix) {
        print "Please enter (y)yes or (n)no\n";
        $response = <>;
        chomp $response; 
        if ($response =~ /n|no/ix) { exit; }
    }
 
    print "Please wait, this may take a few minutes...\n";
    
    # plain-text password generated by ask_diaser_pass()
    my @diaser_passes = ask_diaser_pass(0);
    $user_pass = $diaser_passes[0]; 
 
    passwordless_login($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
    
    gen_fill($config->TZONE_A,
    $config->USER_ACC, $config->DIR_A, $config->FILL_START_TIME, 
    $config->VOLUME_DIR, $config->DIFF_CONST_PREFIX, $config->COLLECT_FULL, 
    $config->COLLECT_FULL_DAY, $config->FULL_PREFIX); 
 
    send_cron_defs($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
    
    gen_hvautoc($config->NUM_YEARS, $config->START_YEAR, $config->HOUR1, 
    $config->HOUR2, $config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C,
    $config->DRY_RUN, $config->LOW_MAX_BW, 
    $config->TZONE_A, $config->TZONE_B, $config->TZONE_C, 
    $config->USER_ACC, $config->TOUT,
    $config->NUM_MONTHS, $config->START_MONTH, $config->START_DOM,   
    $config->COLLECT_FULL_DAY); 
 
    send_hvautoc_algol($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);
 
    send_fill($config->NODE_A, $config->NODE_B, $config->NODE_C,
    $config->PORT_A, $config->PORT_B, $config->PORT_C, $config->USER_ACC);

    return;
}

# write latest variables to configuration file diaser.conf
sub write_to_config {   
    
# unpack scalar parameters
my ($numyears, $startyear, $phase1, $phase2, $nodea, $nodeb, $nodec,
$porta, $portb, $portc, $dryrun, $lmb, $tzone_a, $tzone_b, $tzone_c,
$useracc, $tout, $userdira, $userdirb, $userdirc,
$use_sudo, $sudo_account_name_a, $sudo_account_name_b, $sudo_account_name_c,
$fillstarttime, $volumedir, $diffconstprefix, $collectfull, $collectfullday, 
$fullprefix, $nummachines, $nummonths, $startmonth, $startdom) = @_;

open my $WRITE_CONFIG, '>', 'diaser.conf'
    or croak "Can't create diaser.conf: $OS_ERROR";
print $WRITE_CONFIG <<"_MARKER_";
# Name: diaser.conf 
# Date: 27/04/2010
# Author: Damian L Brasher
# WWW: www.diaser.org.uk http://sourceforge.net/projects/diaser
# Email: dbrasher\@interlinux.co.uk
# License GPL Version 3, 29 June 2007 gpl.txt

# configuration file, edit as required

# number of years of expected operation
NUM_YEARS $numyears

# first year of operation
START_YEAR $startyear 

# times of operation over a 24 hour period, if using more than one diaser
# installation then use different phases or adjust LMB (see below)
# accordingly. The start time is assumed to be your local timezone.

# start time (phase1) first set copies - default 0 
HOUR1 $phase1 

# start time (phase2) second set copies - default 3
HOUR2 $phase2 

# machine IP address's for use in your DIASER pool
NODE_A $nodea 
NODE_B $nodeb 
NODE_C $nodec

# SSH port - default 22
PORT_A $porta 
PORT_B $portb 
PORT_C $portc 

# dry run - if you want to test dry run copies only then leave this set this 
# to 1 otherwise 0
DRY_RUN $dryrun 

# BANDWIDTH control, please enter the Maximum speed in KBPS of your slowest 
# network connection between either A->B or B->C or C->B I recommend you run 
# some test transfers between nodes using scp, also don't assume the
# bandwidth will remain constant throughout the cycle so you may need to run 
# some long term viability tests. This feature will be implemented automatically 
# with the subroutine calculate_lmb(). Adjust if you install more than one diaser 
# instance on a single disk or machine. 
#
# Set here at 12500 KBytes per second / 100 Mbits per second
LOW_MAX_BW $lmb 

# time zone offset - the time zone UTC +/- integer value for node A, B and C, 
# i.e. BST = UTC+1, so use 0. Daylight saving is adjusted automatically
# This variable depends on the node time zone location.
# (display a table of location mapped to non-geo UTC values)
TZONE_A $tzone_a
TZONE_B $tzone_b
TZONE_C $tzone_c

# change this if you install more than one diaser instance per disk diaser 
# working account
USER_ACC $useracc 

# timeout setting for rsync cron jobs, ensures all jobs stop after $tout 
# hours of trying (secs) default 3 hours 
TOUT $tout 

# root directory of diaser account, you may need to adjust if a large 
# partition is not in the usual home directory place i.e. /mnt/big/ will
# evaluate as /mnt/big/diaser
DIR_A $userdira 
DIR_B $userdirb 
DIR_C $userdirc 

# are the machines ubuntu or forced sudo or not?
USE_SUDO $use_sudo

# sudo account name for node A
SUDO_ACCOUNT_NAME_A $sudo_account_name_a

# sudo account name for node B
SUDO_ACCOUNT_NAME_B $sudo_account_name_b

# sudo account name for node C
SUDO_ACCOUNT_NAME_C $sudo_account_name_c

# these variables allow you to setup and control the action of fill_diaser.pl

# time to initiate the daily filling script 
# this should be set in advance of the DIASER archive transfer phases to 
# ensure DIASER is filled before the phases begin
FILL_START_TIME $fillstarttime

# location of volume storage directory is where you store backup volumes
# created by your backup software
VOLUME_DIR $volumedir

# differential or constant volume name prefix
DIFF_CONST_PREFIX $diffconstprefix

# choose whether full volumes are collected or not
# you want to simply collect constant sized volumes, like CCTV footage
COLLECT_FULL $collectfull

# day of month when full volumes are collected
COLLECT_FULL_DAY $collectfullday

# full volume name prefix
FULL_PREFIX $fullprefix

_MARKER_
close $WRITE_CONFIG;

print "\n";
print "New settings have been written to diaser.conf\n";

return;
}

#############################################################################
#
# input validation and tests
#
#############################################################################

# is input a positive integer greater than 0?

sub is_integer {
    
    # match beginning any amount of white-space, more than one numeric,
    # any amount of white-space before newline and greater than 0

    if(( $_[0] =~ /^\s*\d+\s*$/x) and ($_[0] > 0)) {
        return 1;
    }
    else {
        return;
    }
}

# positive integer including zero
sub is_integer_inc_zero {
    
    # match beginning any amount of white-space, more than one numeric,
    # any amount of white-space before newline 

    if( $_[0] =~ /^\s*\d+\s*$/x) {
        return 1;
    }
    else {
        return;
    }
}

# positive and negative integers
sub is_integer_plus_minus {
    
    # match beginning any amount of white-space, more than one numeric,
    # any amount of white-space before newline 

    if( $_[0] =~ /^\s*(\d|-)?(\d)+\s*$/x) {
        return 1;
    }
    else {
        return;
    }
}

# is input a string?
sub is_string {
    
    # match beginning any amount of white-space, more than one alpha-numeric
    # inc _, any number of - char, any amount if alpha-numeric inc _, 
    # any amount of white-space before newline

    if( $_[0] =~ /^\s*\w+-*\w*\s*$/x) {
        return 1;
    }
    else {
        return;
    }
}

# force directory type entry
sub is_directory {
    
    # match beginning \ followed by more than one alpha-numeric inc _, 
    # any number of single \ and alpha-numeric before \ and newline

    if( $_[0] =~ /^\/+\w+(\/?\w*)*\/+$/x) {
        return 1;
    }
    else {
        return;
    }
}

# force IP address type entry, i.e. int.int.int.int
sub is_ip {

    # beginning with 0 or more white space, min 1 - max 3 integers 
    # mandatory . repeated until 3 .s and 4 integers

    if( $_[0] =~ /^\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\s*$/x) {
        return 1;
    }
    else {
        return;
    }
}



