# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::copycds;
use strict;
use warnings;
use Storable qw(dclone);
use xCAT::Table;
use Thread qw(yield);
use Data::Dumper;
use Getopt::Long;
use File::Basename;
use File::Spec;
use Digest::MD5 qw(md5_hex);
use Cwd;
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");

my $xcatdebugmode = 0;
my $processed = 0;
my $callback;

sub handled_commands {
    return {
        copycds => "copycds",
      }
}

my $identified;

sub take_answer {

    #TODO: Intelligently filter and decide things
    my $resp = shift;
    $callback->($resp);
    $identified = 1;
}

sub process_request {
    my $request = shift;
    $callback = shift;
    my $doreq        = shift;
    my $distname     = undef;
    my $arch         = undef;
    my $help         = undef;
    my $inspection   = undef;
    my $path         = undef;
    my $noosimage    = undef;
    my $nonoverwrite = undef;
    my $specific     = undef;

    $identified    = 0;
    $::CDMOUNTPATH = "/var/run/xcat/mountpoint";
    my $existdir = getcwd;

    if ($request->{arg}) {
        @ARGV = @{ $request->{arg} };
    }
    GetOptions(
        'n|name|osver=s' => \$distname,
        'a|arch=s'       => \$arch,
        'h|help'         => \$help,
        'i|inspection'   => \$inspection,
        'p|path=s'       => \$path,
        'o|noosimage'    => \$noosimage,
        's|specific'     => \$specific,
        'w|nonoverwrite' => \$nonoverwrite,
    );
    if ($help) {
        $callback->({ info => "copycds [{-p|--path}=path] [{-n|--name|--osver}=distroname] [{-a|--arch}=architecture] [-i|--inspection] [{-o|--noosimage}] [{-w|--nonoverwrite}] 1st.iso [2nd.iso ...]." });
        return;
    }
    if ($arch and $arch =~ /i.86/) {
        $arch = 'x86';
    }
    my @args = @ARGV;    #copy ARGV
    unless ($#args >= 0) {
        $callback->({ error => "copycds needs at least one full path to ISO currently.", errorcode => [1] });
        return;
    }

    if ($::XCATSITEVALS{xcatdebugmode}) { $xcatdebugmode = $::XCATSITEVALS{xcatdebugmode} }

    my $file;
    foreach (@args) {
        $identified = 0;

        unless (/^\//) { #If not an absolute path, concatenate with client specified cwd
            s/^/$request->{cwd}->[0]\//;
        }

        # /dev/cdrom is a symlink on some systems. We need to be able to determine
        # if the arg points to a block device.
        if (-l $_) {
            my $link = readlink($_);

            # Symlinks can be relative, i.e., "../../foo"
            if ($link =~ m{^/})
            { $file = $link; }
            else
            { $file = dirname($_) . "/" . $link; } # Unix can handle "/foo/../bar"
        }
        else { $file = $_; }

        # handle the copycds for tar file
        # if the source file is tar format, call the 'copytar' command to handle it.
        # currently it's used for Xeon Phi (mic) support
        if (-r $file) {
            my @filestat = `file $file`;
            if (grep /tar archive/, @filestat) {

                # this is a tar file, call the 'copytar' to generate the image
                my $newreq = dclone($request);
                $newreq->{command} = ['copytar']; #Note the singular, it's different
                $newreq->{arg} = [ "-f", $file, "-n", $distname ];
                $doreq->($newreq, $callback);

                return;
            }
            if (grep /$file: data/, @filestat) {
                if ($xcatdebugmode) {
                    $callback->({ info => "run copydata for data file = $file" });
                }
                my $newreq = dclone($request);
                $newreq->{command} = ['copydata']; #Note the singular, it's different
                $newreq->{arg} = [ "-f", $file ];
                if ($inspection)
                {
                    push @{ $newreq->{arg} }, ("-i");
                }
                if ($nonoverwrite)
                {
                    push @{ $newreq->{arg} }, ("-w");
                }
                if ($noosimage)
                {
                    push @{ $newreq->{arg} }, ("-o");
                }

                $doreq->($newreq, $callback);
                return;
            }
        }

        my $mntopts = "-t udf,iso9660"; #Prefer udf formate to iso when media supports both, like MS media
        if (-r $file and -b $file)      # Block device?
        { $mntopts .= " -o ro"; }
        elsif (-r $file and -f $file)    # Assume ISO file
        { $mntopts .= " -o ro,loop"; }
        else {
            $callback->({ error => "The management server was unable to find/read $file. Ensure that file exists on the server at the specified location.", errorcode => [1] });
            return;
        }

        #let the MD5 Digest of isofullpath as the default mount point of the iso
        my $isofullpath = File::Spec->rel2abs($file);
        my $mntpath = File::Spec->catpath("", $::CDMOUNTPATH, md5_hex($isofullpath));

        system("mkdir -p $mntpath");
        system("umount $mntpath >/dev/null 2>&1");



        if (system("mount $mntopts '$file' $mntpath")) {
            eval { $callback->({ error => "copycds was unable to mount $file to $mntpath.", errorcode => [1] }) };
            chdir("/");
            system("umount  $mntpath");
            return;
        }
        eval {
            my $newreq = dclone($request);
            $newreq->{command} = ['copycd'];  #Note the singular, it's different
            $newreq->{arg} = [ "-m", $mntpath ];

            if ($path)
            {

                if(-e $path) {
                    $path=Cwd::realpath($path);
                }
                unless((substr($path,0,length("/install/")) eq "/install/") or ($path eq "/install")){
                    $callback->({ warning => "copycds: the specified path \"$path\" is not a subdirectory under /install. Make sure this path is configured for httpd/apache, otherwise, the provisioning with this iso will fail!" });
                }
                push @{ $newreq->{arg} }, ("-p", $path);
            }
            if ($specific)
            {
                push @{ $newreq->{arg} }, ("-s");
            }

            if ($inspection)
            {
                push @{ $newreq->{arg} }, ("-i");
                $callback->({ info => "OS Image:" . $_ });
            }

            if ($distname) {
                if ($inspection) {
                    $callback->({ warning => "copycds: option --inspection specified, argument specified with option --name is ignored" });
                }
                else {
                    push @{ $newreq->{arg} }, ("-n", $distname);
                }
            }
            if ($arch) {
                if ($inspection) {
                    $callback->({ warning => "copycds: option --inspection specified, argument specified with option --arch is ignored" });
                }
                else {
                    push @{ $newreq->{arg} }, ("-a", $arch);
                }
            }

            if (!-l $file) {
                push @{ $newreq->{arg} }, ("-f", $file);
            }


            if ($noosimage) {
                push @{ $newreq->{arg} }, ("-o");
            }

            if ($nonoverwrite) {
                push @{ $newreq->{arg} }, ("-w");
            }

            $doreq->($newreq, \&take_answer);

            #$::CDMOUNTPATH="";

            chdir($existdir);
            while (wait() > 0) { yield(); } #Make sure all children exit before trying umount
        };
        chdir("/");
        system("umount  $mntpath");
        system("rm -rf $mntpath");
        unless ($identified) {
            $callback->({ error => ["copycds could not identify the ISO supplied, you may wish to try -n <osver>"], errorcode => [1] });
        }
    }
}

1;

# vim: set ts=2 sts=2 sw=2 et ai:
