#!/usr/bin/perl

# Copyright (C) 2002-2014 NEC Corporation
# All Rights Reserved.
#
# [See ftdisk]

require '/opt/nec/ftras/lib/ftdisk_utils.pl';
require '/opt/nec/ftras/lib/simple_menu_utils.pl';

#
my($ftdisk)= "$FTDISK_BINDIR/ftdisk";

#
my(%ftdiskadm_command_list)= (
    raidstat    => ['List RAID Arrays',                  \cmd_raidstat    ],
    stat_sys    => ['List Internal Disks',               \cmd_stat_sys    ],
    create      => ['Make Mirroring Arrays   (RAID1)',   \cmd_create      ],
    repair      => ['Repair Mirroring Arrays (RAID1)',   \cmd_repair      ],
    destroy     => ['Delete Mirroring Arrays (RAID1)',   \cmd_destroy     ],
    remove      => ['Remove Disk Partitions  (RAID1)',   \cmd_remove      ],
    make_stripe => ['Make Striping Array     (RAID1+0)', \cmd_make_stripe ],
    del_stripe  => ['Delete Striping Array   (RAID1+0)', \cmd_del_stripe  ],
    config      => ['Configurations',                    \cmd_config      ],
    mode_quit   => ['Quit',                              \mode_quit       ],
);

my(@ftdiskadm_menu)= (
    ['raidstat=1', 'stat_sys=2', 'create=3', 'repair=4', 'destroy=5', 'remove=6', 'make_stripe=7', 'del_stripe=8', 'config=c', 'mode_quit=q']
);
my($ftdiskadm_menu_index)= 0;

#
unless($ENV{'FTDISKADM_MORE'}){
    my($rows)= 1;
    if(open(C, "stty -a |")){
        while(<C>){
            if(/rows (\d+)/){
                $rows= $1;
                last;
            }
        }
        close(C);
    }
    my($n)= 0;
    foreach (@ftdiskadm_menu){
        if($n < @{$_}){$n= @{$_};};
    }
    $n += 3;

    if($rows > $n){
        $ENV{'FTDISKADM_MORE'}= sprintf("more -%d", $rows - $n);
    }
    else{
        $ENV{'FTDISKADM_MORE'}= 'more';
    }
}
unless($ENV{'AUTO_REPAIR_INTERVAL'}){
    $ENV{'AUTO_REPAIR_INTERVAL'}= 10;
}
$ENV{'FTDISK_PRINT_PROC_COMMAND'}= 1;

#
$ENV{LC_ALL}= 'POSIX';
$SIG{INT}= 'IGNORE';
main();

#
sub main{
    for(;;){
        print "\nCommand action\n";
        my($cmd)= &smuGetKey("Command: ", 0, $ftdiskadm_menu[$ftdiskadm_menu_index],
                              \%ftdiskadm_command_list);
        if($cmd){
            local(*func)= $ftdiskadm_command_list{$cmd}[1];
            &$func();
            print "\n";
        }
    }
}

##
sub mode_quit{ exit(0); }
sub mode_return{$ftdiskadm_menu_index= 0;}
sub mode_scsi{$ftdiskadm_menu_index= 1;}
sub mode_raid{$ftdiskadm_menu_index= 2;}

##
sub cmd_stat_all{
    if(open(MORE, "| $ENV{'FTDISKADM_MORE'}")){
        print MORE "\n[$ftdiskadm_command_list{'stat_all'}[0]]\n";
        print MORE "\nName [Use]    Information (Vendor/Model/Serial)             Path\n";
        print MORE "===============================================================================\n";
        my(@out)= &invoke("$ftdisk stat");
        print MORE $_ foreach(@out);
        close(MORE);
    }
}

##
sub cmd_stat_sys{
    if(open(MORE, "| $ENV{'FTDISKADM_MORE'}")){
        print MORE "\n[$ftdiskadm_command_list{'stat_sys'}[0]]\n";
        print MORE "\nSlot  Name [Use]    Information (Vendor/Model/Serial)             Path\n";
        print MORE "===============================================================================\n";
        my(@out)= &invoke("$ftdisk stat --sys");
        print MORE $_ foreach(@out);
        close(MORE);
    }
}

#
sub cmd_sys_mesg{
    &invokeSystem("$ftdisk sysmesg | $ENV{'FTDISKADM_MORE'}");
}

##
sub cmd_up{
    print "\n[$ftdiskadm_command_list{'up'}[0]]\n";

REINPUT:
    my($path)= &smuGetInput("* Which disk(s)? ['?' for help] => ", 1);
    unless($path){ return; }
    if($path eq '?'){
        print <<HELP;
PATH : h<host>c<channel>t<target>l<lun>

HELP
        goto REINPUT;
    }

    #
    if($path =~ /^h\d+c\d+t\d+l\d+$/){
        &invokeSystem("$ftdisk up $path");
    }
    else{
        print STDERR "Unknown scsi disk: $path\n";
        goto REINPUT;
    }

}

##
sub cmd_down{
    print "\n[$ftdiskadm_command_list{'down'}[0]]\n";

REINPUT:
    my($path)= &smuGetInput("* Which disk(s)? ['?' for help] => ", 1);
    unless($path){ return; }
    if($path eq '?'){
        print <<HELP;
PATH : h<host>c<channel>t<target>l<lun|*>
       
HELP
        goto REINPUT;
    }

    #
    if($path =~ /^h\d+c\d+t\d+l\d+$/){
        &invokeSystem("$ftdisk down $path");
    }
    else{
        print STDERR "Unknown scsi disk: $path\n";
        goto REINPUT;
    }
}

##
sub cmd_config{
    print "\n[$ftdiskadm_command_list{'config'}[0]]\n";

    my(@config)= ("FTDISKADM_MORE=1",
#                  "DISK_SCRUBBING_INTERVAL=2",
#                  "FAST_RESYNC_MD_DEVICES=3",
    );
    my(%disp)= (
        FTDISKADM_MORE =>
            [sprintf("FTDISKADM_MORE          = %s", &param2str('FTDISKADM_MORE')), 0],
#        DISK_SCRUBBING_INTERVAL =>
#            [sprintf("DISK_SCRUBBING_INTERVAL = %s", &param2str('DISK_SCRUBBING_INTERVAL')), 1],
#        FAST_RESYNC_MD_DEVICES =>
#            [sprintf("FAST_RESYNC_MD_DEVICES  = %s", &param2str('FAST_RESYNC_MD_DEVICES')), 2],
    );

    my($var)= &smuGetKey("* Which one? ", 1, \@config, \%disp);
    unless($var){ return; }
    if($disp{$var}->[1] == 0){
        my($val)= &smuGetInput("$var = ", 1);
        if($val){
            $ENV{$var}= $val;
        }
    }
    elsif($disp{$var}->[1] == 1){
        my($val)= &smuGetInput(
                      "$var = ",
                      1,
                      '^(((\d|\w|\,|\/|\-|\*)+\s+){4}(\d|\w|\,|\/|\-|\*)+)$|^on$|^off$');
        if($val eq "on" or $val eq "off"){
            &invokeSystem("$ftdisk scrubbing $val");
        }
        elsif($val){
            &invokeSystem("$ftdisk scrubbing on --interval=\"$val\"");
        }
    }
    elsif($disp{$var}->[1] == 2){
REINPUT:
        my($val)= &smuGetInput(
                      "* What md device number(s) turn on/off? ['?' for help] => ",
                      1,
                      '^\d+(,\d+)*$|^\?$');
        if($val eq "?"){
            print STDOUT <<HINT;
HINT: Mark off raid1 device numbers which you need to turn "fast resync" 
      on/off by comma and push the ENTER key. ( eg. => 1,2,10,11 )

HINT
            goto REINPUT;
        }
        elsif($val){
            my(%mdstat)= &getMdstat();
            foreach (split(/,/, $val)){
                my($md)= "md$_";
                if($mdstat{$md}{'level'} eq '1'){
                    if($mdstat{$md}{'sync_action'} ne 'idle'){
                        printf("%-6s: Currently syncing device\n", $md);
                    }
                    if($mdstat{$md}{'bitmap'}){
                        &invokeSystem("$ftdisk raidopt $md --fast_resync=off");
                    }
                    else{
                        &invokeSystem("$ftdisk raidopt $md --fast_resync=on");
                    }
                }
                else{
                    printf("%-6s: No such RAID1 device", $md);
                }
            }
        }
    }
    else{
    }
    
    #
    sub param2str{
        my($config)= @_;
        
        if($config eq "FTDISKADM_MORE"){
            return $ENV{'FTDISKADM_MORE'};
        }
        elsif($config eq "DISK_SCRUBBING_INTERVAL"){
            my($param)= `$ftdisk scrubbing`;
            if($param =~ /^on \[ (((\d|\w|\,|\/|\-|\*)+\s+){4}(\d|\w|\,|\/|\-|\*)+).+ \]/){
                $param= $1;
                return $param;
            }
            else{
                return "(off)";
            }
        }
        elsif($config eq "FAST_RESYNC_MD_DEVICES"){
            my(%mdstat)= &getMdstat();
            my($cnt)= 1;
            my($params)= "";
            foreach $md (sort devcmp keys %mdstat){
                if($md =~ /^md(\d+)$/ and $mdstat{$md}{'level'} == 1){
                    $params .= sprintf("\n                                ") if($cnt%5 == 0);
                    if($mdstat{$md}{'bitmap'}){
                        $params .= sprintf("%-4d:%-6s", $1, 'on');
                        $cnt++;
                    }
                    else{
                        $params .= sprintf("%-4d:%-6s", $1, 'off');
                        $cnt++;
                    }
                }
            }
            return $params;
        }
        else{
            return undef;
        }
    }
    
    #
    sub devcmp{
        my(@a)= split(/(\d+)$/, $a);
        my(@b)= split(/(\d+)$/, $b);
        ($a[0] cmp $b[0]) or $a[1] <=> $b[1];
    }
}

##
sub cmd_raidstat{
    if(open(MORE, "| $ENV{'FTDISKADM_MORE'}")){
        print MORE "\n[$ftdiskadm_command_list{'raidstat'}[0]]\n";
        print MORE "\nName  ";
        print MORE "Partition       ";
        print MORE "(Label)         ";
        print MORE "Status          ";
        print MORE "Member";
        print MORE "\n";
        print MORE "===============================================================================\n";
        my(@out)= &invoke("$ftdisk raidstat --label --level=0");
        if(0 <= $#out){
            print MORE "< Striping Array  (RAID1+0) >\n";
            print MORE $_ foreach(@out);
        }
        undef(@out);
        my(@out)= &invoke("$ftdisk raidstat --label --level=1");
        if(0 <= $#out){
            print MORE "< Mirroring Array (RAID1)   >\n";
            print MORE $_ foreach(@out);
        }
        close(MORE);
    }
}

##
sub cmd_repair{
    print "\n[$ftdiskadm_command_list{'repair'}[0]]\n";

    my($slt)= &smuGetNumber("* Which SCSI SLOT? [1-16] ", 1, 1, 16);
    if($slt > 0){
        if(&invokeSystem("$ftdisk repair $slt") == 2){
            if(&smuGetYesNo("* Are you sure to repair it? [y/n] ", 1) > 0){
                &invokeSystem("$ftdisk repair --force $slt");
            }
        }
    }
}

##
sub cmd_auto_repair{
    print "\n[$ftdiskadm_command_list{'auto_repair'}[0]]\n";

    my($raidrepair)= '/etc/cron.d/raidrepair';
    my($on)= (-f $raidrepair);
    if($on){
        if(&smuGetYesNo("* Are you sure to turn off this function? [y/n] ", 1) > 0){
            &invokeSystem("$ftdisk autorepair off");
        }
    }
    else{
        if(&smuGetYesNo("* Are you sure to turn on this function? [y/n] ", 1) > 0){
            &invokeSystem("$ftdisk autorepair on");
        }
    }
}

##
sub cmd_remove{
    print "\n[$ftdiskadm_command_list{'remove'}[0]]\n";

    my($slt)= &smuGetNumber("* Which SCSI SLOT? [1-16] ", 1, 1, 16);
    if($slt > 0){
        if(&invokeSystem("$ftdisk remove $slt") == 2){
        #  if(&smuGetYesNo("* Are you sure to remove it? [y/n] ", 1) > 0){
        #      &invokeSystem("$ftdisk remove --force $slt");
        #  }
        }
    }
}

##
sub cmd_create{
    print "\n[$ftdiskadm_command_list{'create'}[0]]\n";

    my($slt)= &smuGetNumber("* Which scsi SLOT? [1-16] ", 1, 1, 16);
    if($slt > 0){
        my(@sizes)= &getPartitionSizes($slt);
        if(@sizes > 0){
            shift(@sizes);
            my($s)= join(',', @sizes);
            my($label)= &smuGetInput("  * Input the LABEL [1-12 character(s)] ", 1, '^\S{1,12}$');
            if(&smuGetYesNo("* Are you sure to create it? [y/n] ", 1) > 0){
                &invokeSystem("$ftdisk create --size=$s --label=$label $slt");
            }
        }
        else{
            &invokeSystem("$ftdisk create --size=0,-1 $slt");
        }
    }
}

#
sub getPartitionSizes{
    my($slt)= @_;

    my($rest_size)= &getScsiDiskSize($slt);
    my($least_size, $least_size_last)= (1, 1024);

    if($rest_size == 0){
        return;
    }

    print "\nMaking the disk partition table: SLOT=$slt SIZE=$rest_size(MB)\n";
    print "      Reserved for the last partition: SIZE=$least_size_last(MB)\n";
    my($num)= &smuGetNumber("  * How many partitions? [1-14] ", 0, 1,14);

    $rest_size -= ($least_size * ($num-1)) + $least_size_last;
    my(@sizes);
    my($idx)= 1;
    while($num-- > 1){
        $rest_size += $least_size;
        my($msg)= sprintf("    * Input the SIZE of partition %-2d [$least_size-%6d(MB)] ", $idx, $rest_size);
        $sizes[$idx]= &smuGetNumber($msg, 0, $least_size, $rest_size);
        $rest_size -= $sizes[$idx];
        if((++$idx == 4) and ($num > 1)){
            $idx= 5;
        }
    }
    $rest_size += $least_size_last;
    printf("                        partition %-2d                $rest_size\n", $idx);
    $sizes[$idx]= -1;

    return @sizes;
}

##
sub cmd_destroy{
    print "\n[$ftdiskadm_command_list{'destroy'}[0]]\n";

    my($slt)= &smuGetNumber("* Which SCSI SLOT? [1-16] ", 1, 1, 16);
    if($slt > 0){
        if(&invokeSystem("$ftdisk destroy $slt") == 2){
           #if(&smuGetYesNo("* Are you sure to remove them? [y/n] ", 1) > 0){
           #    &invokeSystem("$ftdisk destroy --force $slt");
           #}
        }
    }
}

##
sub cmd_make_stripe{
    print "\n[$ftdiskadm_command_list{'make_stripe'}[0]]\n";

REINPUT:
    my($mds)= &smuGetInput(
                  "* Which raid1 device numbers? ['?' for help] => ", 
                  1,
                  '^\d+(\,\d+)*$|^\?$');
    if($mds eq '?'){
        print STDOUT <<HINT;
HINT: Mark off raid1 device numbers for the striping array by a comma 
      and push the ENTER key. ( eg. => 1,2,10,11 )

HINT
        goto REINPUT;
    }
    elsif($mds ne ''){
        &invokeSystem("$ftdisk striping --create=$mds");
    }
}

##
sub cmd_del_stripe{
    print "\n[$ftdiskadm_command_list{'del_stripe'}[0]]\n";

REINPUT:
    my($md)= &smuGetInput(
                  "* Which raid1+0 device numbers? ['?' for help] => ", 
                  1,
                  '^\d+$|^\?$');
    if($md eq '?'){
        print STDOUT <<HINT;
HINT: Type raid1+0 device number to delete and push the ENTER key.
      eg. => 5 (If you need to delete md5 device)

HINT
        goto REINPUT;
    }
    elsif($md ne ''){
        &invokeSystem("$ftdisk striping md$md --delete");
    }
}

#
sub invoke{
    if(open(CMD, "@_ |")){
        my(@out)= <CMD>;
        close(CMD);
        return @out;
    }
    else{
        $? = 1 << 8;
        return ();
    }
}

#
sub invokeSystem{
    my($rc)= system(@_);
    if($rc & 0xff){
        return -($rc & 0xff);
    }
    elsif($rc >> 8){
        return $rc >> 8;
    }
    else{
        return 0;
    }
}

