#!/usr/bin/env perl
BEGIN
{
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
    $::PERL_VENDOR_PRIVLIB = $ENV{'PERL_VENDOR_PRIVLIB'} ? $ENV{'PERL_VENDOR_PRIVLIB'} : "$::XCATROOT/lib/perl";
}
use lib "$::XCATROOT/lib/perl";
use lib "$::XCATROOT/share/xcat/netboot/imgutils";

use File::Basename;
use xCAT::Table;
use File::Path;
use File::Path qw(remove_tree);
use File::Copy;
use File::Find;
use Getopt::Long;
use Cwd qw(realpath);
use Template;
use File::Temp qw/ tempdir /;
use File::Copy::Recursive qw(dircopy);
use imgutils;

# More informative error messages
# use diagnostics;

Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");

# Functions prototypes
sub tarball($$);

# `linuximage' values
my $pkglist;
my $pkgdir;
my $otherpkgdir;
my $otherpkglist;
my $postinstall;
my $rootimgdir;
my $nodebootif; #TODO be flexible on node primary nic
my $otherifce; #TODO be flexible on node primary nic
my $netdrivers;
my $permission; # permission works only for statelite mode currently
my $kernelver = "";

my $kernel_base_version; # 2.6.30
my $kernel_release;      # alt1
my $kernel_flavour;      # hpc-skif

my @nics;
my @ndrivers;
my $arch;
my $profile;
my $provmethod;
my $osver;
my $pathtofiles = dirname($0);
my $fullpath    = realpath($pathtofiles);
my $name        = basename($0);
my $onlyinitrd  = 0;
if ($name =~ /geninitrd/) {
    $onlyinitrd=1;
}
my $rootlimit;
my $tmplimit;
my $installroot;
my $customdir=$fullpath;
my $imagename;
my $rootimg_dir;
my $verbose;
my $help;
my $no_cleanup = 0;
my $cleanup = 1;

# Hash for updating `osimage' table
my %updates_os = ();

# Hash for updating `linuximage' table
my %updates = ();

##
# ALTLinux Predefined variables
#

# Script name
my $PROG = basename(__FILE__);

# mkimage template profile location, according to hasher user home dir
my $PROFILE = "mkimage-profile-diskless";

# Hasher user created during RPM package installation
my $HSHUSR  = "_mknetboot";

# Create mkimage profile according with user configuration files:
my $INITRD_PACKAGES = "/etc/xcat/alt/diskless-initrd-packages";
my $INITRD_MODULES  = "/etc/xcat/alt/diskless-initrd-modules";
my $INITRD_FILES    = "/etc/xcat/alt/diskless-initrd-files";

# Default network drivers. Used when user doesn't specified `-n' option
@default_net_drivers = qw/tg3 bnx2 bnx2x e1000 e1000e igb/;

# Default transport method to access NFS server. Maybe: `udp' or `rdma'
my $nfs_transport="udp";

# Temporary directory with generated mkimage profile at predefined path.
# Default: do not use predefined tmp dir.
my $predefined_tmpdir = "undefined";

##
# Program start here
#

# Parse command line options
$ret = GetOptions(
    'a=s'       => \$arch,
    'p=s'       => \$profile,
    'o=s'       => \$osver,
    'n=s'       => \$netdrivers,
    'i=s'       => \$nodebootif,
    'r=s'       => \$otherifce,
    'l=s'       => \$rootlimit,
    't=s'       => \$tmplimit,
    'k=s'       => \$kernelver,
    'h|help'    => \$help,
    'v|verbose' => \$verbose,
    'd|no-cleanup' => \$no_cleanup,
    'permission=s' => \$permission,
    'N|nfs-transport=s' => \$nfs_transport,
    'f|predefined-tmp:s' => \$predefined_tmpdir,
);

unless ($ret) {
    printf (STDERR "Use: %s <-h|--help> for more information\n", "$PROG");
    exit 1;
}

if ( "$nfs_transport" ne "udp" and "$nfs_transport" ne "rdma" and "$nfs_transport" ne "tcp" ) {
    printf (STDERR "Unknown NFS transport: $nfs_transport\n");
    exit 1;
}

# Did user ask for help?
if ($help) {
    &usage;
    exit 0;
}

# Lookup for xCAT database for site.installdir value
$installroot  = xCAT::Utils->getInstallDir();
if ( "$installroot" eq "" ) {
    die "$PROG: Failed to lookup xcat `site' table for installdir location.";
}
unless ( -d "$installroot" ) {
    die "$PROG: Can't find  $installroot directory.";
}

# Update `linuximage' and `osimage' tables according to command line arguments
my $needUpdateTable = 0;

my $osimagetab;
my $linuximagetab;
my $ref_linuximage_tab;
my $ref_osimage_tab;

# Load Table.pm module
eval { require("$::PERL_VENDOR_PRIVLIB/xCAT/Table.pm") };
unless ($@) {
    # Table.pm is there, we can update the xCAT tables
    $needUpdateTable = 1;

    # Open `osimage' table
    $osimagetab = xCAT::Table->new('osimage', -create=>1);
    unless ($osimagetab) {
        print "$PROG: The osimage table cannot be opened.\n";
        exit 1;
    }

    # Open `linuximage' table
    $linuximagetab = xCAT::Table->new('linuximage', -create=>1);
    unless ($linuximagetab) {
        print "$PROG: The linuximage table cannot be opened.\n";
        exit 1;
    }
}

if (@ARGV > 0 and $needUpdateTable eq 1) {
    my $site_ref;
    $imagename=$ARGV[0];

    if ($arch or $osver or $profile)
    {
        printf (STDERR "$PROG: -o, -p and -a options are not allowed when a image name is specified.\n");
        exit 1;
    }

    # Path to custom templates
    # XXX@stanv
    $customdir =~ s/.*share\/xcat/$installroot\/custom/;
    print "$PROG: User may define own templates at $customdir directory\n";

    # Read information for imagename from `osimage' table
    ($ref_osimage_tab) = $osimagetab->getAttribs({imagename => $imagename}, 'osvers', 'osarch', 'profile', 'provmethod');
    unless ($ref_osimage_tab) {
        print "$PROG: Cannot find image \'$imagename\' from the osimage table.\n";
        exit 1;
    }

    # Read information for imagename from `linuximage' table
    ($ref_linuximage_tab) = $linuximagetab->getAttribs({imagename => $imagename}, 'pkglist', 'pkgdir', 'otherpkglist', 'otherpkgdir', 'postinstall', 'rootimgdir', 'nodebootif', 'otherifce', 'kernelver', 'netdrivers', 'permission');
    unless ($ref_linuximage_tab) {
        print "$PROG: Cannot find $imagename from the linuximage table\n";
        exit 1;
    }

    # Ask values from tables
    # `osimge' values
    $osver      = $ref_osimage_tab->{'osvers'};
    $arch       = $ref_osimage_tab->{'osarch'};
    $profile    = $ref_osimage_tab->{'profile'};
    $provmethod = $ref_osimage_tab->{'provmethod'};

    # `linuximage' unmodified values
    $pkglist              = $ref_linuximage_tab->{'pkglist'};
    $pkgdir               = $ref_linuximage_tab->{'pkgdir'};
    $otherpkgdir          = $ref_linuximage_tab->{'otherpkgdir'};
    $otherpkglist         = $ref_linuximage_tab->{'otherpkglist'};
    $postinstall          = $ref_linuximage_tab->{'postinstall'};
    $rootimgdir           = $ref_linuximage_tab->{'rootimgdir'};

    # `linuximage' values, that can be changed via command line options
    my $nodebootif_cur       = $ref_linuximage_tab->{'nodebootif'};
    my $otherifce_cur        = $ref_linuximage_tab->{'otherifce'};
    my $kernelver_cur        = $ref_linuximage_tab->{'kernelver'};
    my $netdrivers_cur       = $ref_linuximage_tab->{'netdrivers'};
    my $permission_cur       = $ref_linuximage_tab->{'permission'};

    unless ($osver and $arch and $profile and $provmethod) {
        printf (STDERR "$PROG: osimage.osvers, osimage.osarch, osimage.profile and osimage.provmethod must be specified for the image $imagename in the database.\n");
        exit 1;
    }

    if ($provmethod ne 'netboot' and $provmethod ne 'statelite' ) {
        printf (STDERR "$PROG: \'$imagename\' cannot be used to build diskless image. Make sure osimage.provmethod is 'netboot' or 'statelite'.\n");
        exit 1;
    }

    unless ($pkglist) {
        printf (STDERR "$PROG: A .pkglist file must be specified for image \'$imagename\' in the linuximage table.\n");
        exit 0;
    }

    # Update linuximage.nodebootif with -i <primary nic>
    if ($nodebootif) {
        if ($nodebootif ne $nodebootif_cur) {
            print "$PROG: The primary nic is different from the value in linuximage.nodebootif table, will update it\n";
            print "$PROG: Old linuximage.nodebootif: $nodebootif_cur\n";
            print "$PROG: New linuximage.nodebootif: $nodebootif\n";
            $updates{'nodebootif'} = $nodebootif;
        }
    } else {
        $nodebootif = $nodebootif_cur;
    }

    # Update linuximage.otherifce with -r <other nics>
    if ($otherifce) {
        if ($otherifce ne $otherifce_cur) {
            print "$PROG: The other ifces are different from  the value in linuximage.otherifce table, will update it\n";
            print "$PROG: Old linuximage.otherifce: $otherifce_cur\n";
            print "$PROG: New linuximage.otherifce: $otherifce\n";
            $updates{'otherifce'} = $otherifce;
        }
    } else {
        $otherifce = $otherifce_cur;
    }

    # Update linuximage.kernelver with -k <kernelver>
    if ($kernelver) {
        if ($kernelver ne $kernelver_cur) {
            print "$PROG: The kernelver is different from the value in linuximage.kernelver table, will update it\n";
            print "$PROG: Old linuximage.kernelver: $kernelver_cur\n";
            print "$PROG: New linuximage.kernelver: $kernelver\n";
            $updates{'kernelver'} = $kernelver;
        }
    } else {
        $kernelver = $kernelver_cur;
    }

    # Update linuximage.netdrivers with -n <nodenetdrivers>
    if ($netdrivers) {
        if ($netdrivers ne $netdrivers_cur) {
            print "$PROG: The netdrivers are different from the value in linuximage.netdrivers table, will update it\n";
            print "$PROG: Old linuximage.netdrivers: $netdrivers_cur\n";
            print "$PROG: New linuximage.netdrivers: $netdrivers\n";
            $updates{'netdrivers'} = $netdrivers;
        }
    } else {
        $netdrivers = $netdrivers_cur;
    }

    # Update linuximage.permission with --permission <permission>
    if ($permission) {
        if ($permission ne $permission_cur) {
            print "$PROG: The permission value is different from the value in linuximage.permission table, will update it\n";
            print "$PROG: Old linuximage.permission: $permission_cur\n";
            print "$PROG: New linuximage.permission: $permission\n";
            $updates{'permission'} = $permission;
        }
    } else {
        $permission = $permission_cur;
    }
}

# Does all necessary varibles are defined ?
unless ($osver and $profile) {
    printf (STDERR "$PROG: Not enough arguments. \n");
    printf (STDERR "$PROG: Use: %s <-h|--help> for more information\n", "$PROG");
    exit 1;
}

# Default TMPFS permission mounted at /.statelite.
unless ($permission) {
    $permission = "755";
    print "$PROG: Using default permission = $permission for /.statelite\n";
    if ($needUpdateTable) {
        $updates{'permission'} = $permission;
        print "$PROG: New linuximage.permission: $permission\n";
    }
}

print "$PROG: Key site.installdir point to $installroot\n";

# Get hasher user ID
my $hshuid  = (getpwnam("$HSHUSR"))[2];
unless ($hshuid) {
    die "Can't get UID for $HSHUSR\n";
}

# Get hasher user primary GID
my $hshgid  = (getpwnam("$HSHUSR"))[3];

# Get hasher user home directory
my $hshhome = (getpwnam("$HSHUSR"))[-2];

print "$PROG: Build mkimage profile with $HSHUSR user, UID: $hshuid, GID: $hshgid, HOME: $hshhome \n";

# Check for mkimage profile template
unless ( -d "$hshhome/$PROFILE" ) {
    die "$PROG: Can't locate directory with mkimage profile at $hshhome/$PROFILE";
}

# Cleanup mkimage work dir?
if ( $no_cleanup or $predefined_tmpdir ne "undefined" ) {
    $cleanup = 0;
    $no_cleanup = 1;
}

# Build mkimage profile at tempdir
my $tempdir;
# User want to use predefined build dir.
unless ( "$predefined_tmpdir" eq "undefined" ) {
    # User didn't supplies location. Use default one.
    unless ( $predefined_tmpdir ) {
        $predefined_tmpdir="$hshhome/$PROG-build/$arch";
    }

    if ( -d "$predefined_tmpdir" ) {
        die "Sorry, $predefined_tmpdir already exist. Please remove it first.";
    }

    mkpath ( "$predefined_tmpdir", {error => \my $err} );
    if (@$err) {
        die "Can't create tempory directory for build purposes";
    }
    $tempdir="$predefined_tmpdir";
} else {
        # Create temporary build directory
        $tempdir = tempdir ( "$PROG.XXXXXX", DIR => "$hshhome", CLEANUP => $cleanup )
            or die "Cant create tempory directory for build purposes";
}

# Change owner for tempdir
chown $hshuid, $hshgid, "$tempdir";
print "$PROG: Create temporary directory: $tempdir\n";

# Copy mkimage profile to temporary directory
print "$PROG: Copy `mkimage' profile $hshhome/$PROFILE to $tempdir\n";
dircopy("$hshhome/$PROFILE","$tempdir") or die $!;

##
# Back to defaults for undefined variables
#

# Fetch arch info
unless ($arch) {
    $arch = `uname -m`;
    chomp($arch);
    if ($arch =~ /i.86$/) {
        $arch = "x86";
    }
}

# linuximage.pkgdir - the name of the directory where the distro packages are stored
unless ($pkgdir) {
    $pkgdir="$installroot/$osver/$arch";
    if ($needUpdateTable) {
        $updates{'pkgdir'} = $pkgdir;
        print "$PROG: New linuximage.pkgdir: $pkgdir\n";
    }
}

# XXX@stanv: TODO
# linuximage.otherpkgdir - the base directory where the non-distro packages are stored.
unless ($otherpkgdir) {
    $otherpkgdir = "$installroot/post/otherpkgs/$osver/$arch";
    if ($needUpdateTable) {
        $updates{'otherpkgdir'} = $otherpkgdir;
        print "$PROG: New linuximage.otherpkgdir: $otherpkgdir\n";
    }
}

# linuximage.rootimgdir - the directory name where the image is stored
unless ($rootimgdir)
{
    $rootimgdir="$installroot/netboot/$osver/$arch/$profile";
    if ($needUpdateTable) {
        $updates{'rootimgdir'} = $rootimgdir;
        print "$PROG: New linuximage.rootimgdir: $rootimgdir\n";
    }
}

# Unpacked image location for target profile
$rootimg_dir="$rootimgdir/rootimg";
print "$PROG: Upacked root file system for $profile profile is $rootimg_dir\n";

# $kernelver must have format: `2.6.27-hpc-skif-alt3'. This is xCAT genimage requirement
unless ($kernelver) {
    $kernelver = `uname -r`;
    chomp($kernelver);
}

if ($needUpdateTable) {
    $updates{'kernelver'} = $kernelver;
    print "$PROG: New linuximage.kernelver: $kernelver\n";
}

$kernel_base_version = $kernel_release = $kernel_flavour = $kernelver;
# hpc-skif
$kernel_flavour =~ s/([[:digit:]]|\.)+-(.+)-[[:alnum:]]+$/$2/;
# 2.6.30
$kernel_base_version =~ s/-.+$//;
# alt1
$kernel_release =~ s/^.+-([[:alnum:]]+)$/$1/;
print "$PROG: Kernel info: flavour=$kernel_flavour, version=$kernel_base_version, release=$kernel_release\n";

##
# Varibles for macrosses substitution in mkimage profile
#

# Directory with initrd and kernel image
my $m_resultsdir = "$hshhome";
# mkimage target
my $m_arch = "$arch";
# Verbose flag
my $m_verbose;
# Kernel image name
my $m_kernel = "diskless-kernel.$arch";
# Put generated initrd image under name
my $m_initrd = "diskless-initrd.$arch";
# Name for image with root file system
my $m_rootfs = "diskless-rootfs.$arch";

if ( $verbose ) {
    $m_verbose = "Yes";
}

# Generate network drivers list
if ($netdrivers) {
    if ( ($updates{'netdrivers'} ne $netdrivers) and ($needUpdateTable) ) {
        $updates{'netdrivers'} = $netdrivers;
        print "$PROG: New linuximage.netdrivers: $netdrivers\n";
    }
} else {
    @ndrivers = @default_net_drivers;
}

foreach (split /,/,$netdrivers) {
   unless (/\.ko$/) {
      s/$/.ko/;
   }
   if (/^$/) {
      next;
   }
   push @ndrivers, $_;
}

print "$PROG: Initrd image will have next kernel modules: @ndrivers\n";

# Generate network interfaces list
foreach (split /,/,$otherifce) {
   if (/^$/) {
      next;
   }
   push @nics, $_;
}
print "$PROG: Initrd image will uses $nodebootif as primary net interface, additional interfsces: @nics\n";

# Create and build mkimage profile
unless ($onlyinitrd) {
    my $apt_src_list;

    # Open `site' table
    my $sitetab=xCAT::Table->new('site');
    if (!$sitetab) {
        printf (STDERR "$PROG: The `site' table cannot be opened.\n");
        exit 1;
    }

    # Scan `site' table for APT sources list file
    # XXX@stanv: how to be with various branches?
    $site_ref = $sitetab->getAttribs({key => "aptsrclist_$arch"}, 'value');
    if ($site_ref and $site_ref->{value})
    {
        my $db_src_list = $site_ref->{value};

        # Open sources.list from `site' table for reading
        open ( SRC, "<", "$db_src_list" ) or
            die "$PROG: Could not open file $db_src_list: $!\n";

        # Append to destionation sources.list
        open ( DST, ">>", "$tempdir/sources.list") or
            die "$PROG: Could not open file $tempdir/sources.list: $!\n";

        # Read each line of SRC and add it to DST
        while ( my $line = <SRC> ) {
            print DST $line;
        }

        print "$PROG: Created source.list for APT: $tempdir/sources.list from $db_src_list\n";

        close(SRC);
        close(DST);
    }

    # Get packages list
    unless ($imagename) {
        $pkglist= imgutils::get_profile_def_filename($osver, $profile, $arch, $customdir, "pkglist");
        unless ($pkglist) {
            $pkglist= imgutils::get_profile_def_filename($osver, $profile, $arch, $pathtofiles, "pkglist");
        }
    }
    unless ($pkglist) {
        printf (STDERR "$PROG: Unable to find package list for $profile!\n");
        exit 1;
    }

    if ($needUpdateTable) {
        $updates{'pkglist'} = $pkglist;
        print "$PROG: New linuximage.pkglist: $pkglist\n";
    }

    print "$PROG: Build image with package list: $pkglist\n";

    # Get additional packages list
    unless ($imagename) {
        $otherpkglist=imgutils::get_profile_def_filename($osver, $profile, $arch, $customdir,"otherpkgs.pkglist");
        unless ($otherpkglist) {
            $otherpkglist=imgutils::get_profile_def_filename($osver, $profile, $arch, $pathtofiles, "otherpkgs.pkglist");
        }
    }
    if ($otherpkglist) {
        if ($needUpdateTable) {
            $updates{'otherpkglist'} = $otherpkglist;
            print "$PROG: New linuximage.otherpkglist: $otherpkglist\n";
        }
        print "$PROG: Build image with extra packages list: $pkglist\n";
    }

    # Expand macroses in packages lists
    my %pkg_hash = imgutils::get_package_names($pkglist);
    my %other_pkg_hash;
    if ($otherpkglist) {
        %other_pkg_hash = imgutils::get_package_names($otherpkglist);
    }

    open (DST, ">>", "$tempdir/packages") or
      die "$PROG: Could not open file $tempdir/packages: $!\n";

    foreach $pass (sort (keys(%pkg_hash)), sort (keys(%other_pkg_hash))) {
        foreach (keys(%{$pkg_hash{$pass}})) {
            my $remove="";
            my $pa=$pkg_hash{$pass}{$_};
            if (($_ eq "PRE_REMOVE") || ($_ eq "POST_REMOVE")) {
                $remove="-";
            }
            foreach $package (@$pa) {
                printf (DST $remove . $package . "\n");
            }
        }
    }
    close (DST);

    # mkimage wants i586, xCAT wants x86
    $m_arch =~ s/^x86$/i586/;

    # Expand macroses in templates
    my $vars_Makefile = {
        arch               => "$m_arch",
        verbose            => "$m_verbose",
        resultsdir         => "$m_resultsdir",
        name_initrd        => "$m_initrd",
        name_kernel        => "$m_kernel",
        name_rootfs        => "$m_rootfs",
        kernel             => "kernel-image-$kernel_flavour",
    };

    my $vars_aptconf = {
        srclist => "$tempdir/sources.list",
    };

    my $vars_init = {
        prinic        => $nodebootif,
        rootlimit     => $rootlimit,
        modules       => "@ndrivers",
        permission    => $permission,
        tmplimit      => $tmplimit,
        nfs_transport => $nfs_transport,
    };

    my $tt = Template->new({
            INCLUDE_PATH => "$tempdir",
            OUTPUT_PATH  => "$tempdir",
        }) || die "$Template::ERROR\n";

    $tt->process("Makefile.in", $vars_Makefile, "Makefile")   || die $tt->error();
    $tt->process("apt.conf.in", $vars_aptconf, "apt.conf")    || die $tt->error();
    $tt->process("initrd-init.in", $vars_init, "initrd-init") || die $tt->error();

    # Copy another parts for mkimage profile
    foreach my $config ($INITRD_PACKAGES, $INITRD_MODULES, $INITRD_FILES) {
        my $dst = $config;
        $dst =~ s/.*diskless-//;
        unless ( -f "$config" ) {
            die "$PROG: Could not find file $config: $!\n";
        }
        print "$PROG: copy $config to $tempdir/$dst\n";
        copy("$config", "$tempdir/$dst") or die "Copy failed: $!";
    }

    # Add to list modules specified via command line
    open ( DST, ">>", "$tempdir/initrd-modules") or die "$PROG: Could not open file $tempdir/sources.list: $!\n";
    printf (DST "# Below modules was specified via command line for $PROG\n");
    foreach my $module (@ndrivers) {
        printf (DST "$module\n");
    }
    close (DST);

    # Remove any previsious images
    foreach my $image ($m_initrd, $m_kernel, $m_rootfs) {
        if ( -f "$m_resultsdir/$image" ) {
            print "$PROG: Remove previsious image: $m_resultsdir/$image\n";
            unlink "$m_resultsdir/$image";
        }
    }

    # mkimage: build-image
    system("su", "-s", "/bin/sh", "-l", "$HSHUSR", "-c", "/usr/bin/make -C $tempdir") == 0
        or die "mkimage: build-image failed";

    # Test results
    foreach my $image ($m_initrd, $m_kernel, $m_rootfs) {
        unless ( -f "$m_resultsdir/$image" ) {
            die "$PROG: Can't find results from mkimage build ($m_resultsdir/$image)";
        }
    }

    # Cleanup mkimage workdir
    if ( $cleanup ) {
        system("su", "-s", "/bin/sh", "-l", "$HSHUSR", "-c", "/usr/bin/make clean -C $tempdir") == 0
            or die "mkimage: clean failed";
    }

    # Unpack result image with rootfs to $rootimg_dir
    unless ( -d "$rootimg_dir" ) {
        mkpath("$rootimg_dir", {error => \my $err});
        if (@$err) {
            die "Failed to crate $rootimg_dir";
        }
    }
    print "$PROG: Unpack rootfs image $m_resultsdir/$m_rootfs to $rootimg_dir\n";
    tarball("$m_resultsdir/$m_rootfs", "$rootimg_dir");

    # Cleanup previsious packimage results
    foreach my $suffix ("sfs", "gz") {
        if ( -f "$rootimgdir/rootimg.$suffix" ) {
            print "$PROG: Remove previsious packimage image: $rootimgdir/rootimg.$suffix\n";
            unlink "$rootimgdir/rootimg.$suffix";
        }
    }

    # Run postscripts
    print "$PROG: Running post-scripts...\n";
    postscripts();
}

# Run linuximage.postinstall script
unless ($imagename) {
    $postinstall=imgutils::get_profile_def_filename($osver, $profile, $arch, $customdir, "postinstall");
    unless ($postinstall) {
        $postinstall=imgutils::get_profile_def_filename($osver, $profile, $arch, $pathtofiles, "postinstall");
    }
}

if (($postinstall) && (-x $postinstall)) {
    if ($needUpdateTable) {
        $updates{'postinstall'} = $postinstall;
        print "$PROG: New linuximage.postinstall: $postinstall\n";
    }
    print "$PROG: exec postinstall script $postinstall at $rootimg_dir \n";
    my $rc = system($postinstall, $rootimg_dir,$osver,$arch,$profile);
    if($rc) {
        printf (STDERR "$PROG: postinstall script failed\n");
        exit 1;
    }
}

# Commit the changes to the linuximage/osimage table if necessary
if ($needUpdateTable) {
    my %keyhash = ();

    # All the attributes have been gathered.
    # Update `linuximage' and `osimage' tables.
    if ($imagename) {
        $keyhash{'imagename'} = $imagename;
        $linuximagetab->setAttribs(\%keyhash, \%updates);
        $linuximagetab->commit;
    } else {
        my $key;
        my $value;

        # update the imagename for stateless
        $keyhash{'imagename'} = "$osver-$arch-netboot-$profile";

        $updates_os{'profile'}    = $profile;
        $updates_os{'imagetype'}  = 'linux';
        $updates_os{'provmethod'} = 'netboot';
        $updates_os{'osname'}     = 'Linux';
        $updates_os{'osvers'}     = $osver;
        $updates_os{'osdistro'}   = 'alt';
        $updates_os{'osarch'}     = $arch;

        print "$PROG: update osimage table for @{[%keyhash]}:\n";
        while(($key,$value) = each (%updates_os))
        {
            print "$PROG: $key = $value\n";
        }
        $osimagetab->setAttribs(\%keyhash, \%updates_os);
        $osimagetab->commit;

        print "$PROG: update linuximage table for @{[%keyhash]}:\n";
        while(($key,$value) = each (%updates))
        {
            print "$PROG: $key = $value\n";
        }
        $linuximagetab->setAttribs(\%keyhash, \%updates);
        $linuximagetab->commit;

        # update the imagename for statelite
        $keyhash{'imagename'} = "$osver-$arch-statelite-$profile";

        $updates_os{'provmethod'} = 'statelite';

        print "$PROG: update osimage table for @{[%keyhash]}:\n";
        while(($key,$value) = each (%updates_os))
        {
            print "$PROG: $key = $value\n";
        }
        $osimagetab->setAttribs(\%keyhash, \%updates_os);
        $osimagetab->commit;

        print "$PROG: update linuximage table for @{[%keyhash]}:\n";
        while(($key,$value) = each (%updates))
        {
            print "$PROG: $key = $value\n";
        }
        $linuximagetab->setAttribs(\%keyhash, \%updates);
        $linuximagetab->commit;
    }
}

# Initrd and kernel for netboot

# One kernel for statelite / stateless
copy("$m_resultsdir/$m_kernel", "$rootimgdir/kernel") or die "Failed to provide kernel: $!";
print "$PROG: Copy kernel to $rootimgdir/kernel\n";

# One initrd for statelite / stateless
copy("$m_resultsdir/$m_initrd", "$rootimgdir/initrd.gz") or die "Failed to provide initrd: $!";
print "$PROG: Copy initrd to $rootimgdir/initrd.gz\n";

# XXX@stanv: at this moment initrd image doesn't have difference for statelite/stateless
foreach $mode ('statelite', 'stateless') {
    copy("$m_resultsdir/$m_initrd", "$rootimgdir/initrd-$mode.gz") or die "Failed to provide initrd: $!";
    print "$PROG: Copy initrd to $rootimgdir/initrd-$mode.gz\n";
}

# Add `.statelite' directory, this is where tmpfs will be created.
# create place for NFS mounts.
mkpath "$rootimg_dir/.statelite";

# create place for NFS mounts for ssh;
mkpath "$rootimg_dir/root/.ssh";

# this script will get the directories.
unless(-f "$pathtofiles/../add-on/statelite/rc.statelite"){
    printf (STDERR "$PROG: Can't find $pathtofiles/../add-on/statelite/rc.statelite!\n");
    exit 1;
}
print "$PROG: Copy rc.statelite to $rootimg_dir/etc/init.d/statelite\n";
system("cp $pathtofiles/../add-on/statelite/rc.statelite $rootimg_dir/etc/init.d/statelite");

if ( $no_cleanup ) {
    print "$PROG: Do not cleanup temporary directory ($tempdir). Please cleanup manually.\n";
}

#
# END OF PROGRAM HERE
#

# Show helpful information
sub usage {
    print "Usage: $PROG [-i <nodebootif>] [-n <nodenetdrivers>] [-r <otherifaces>] -o <OSVER> -p <PROFILE> [-k <KERNELVER>] [--permission <permission>]\n";
    print "       $PROG [-i <nodebootif>] [-n <nodenetdrivers>] [-r <otherifaces>] [-k <KERNELVER>] <imagename>\n";
    print "Examples:\n";
    print "       $PROG -i eth0 -n tg3 -o alt -p compute\n";
    print "       $PROG -i eth0 -r eth1,eth2 -n tg3,bnx2 -o alt -p compute\n";
    print "       $PROG -i eth0 -n tg3 myimage\n";
    print "       $PROG -k 2.6.27-hpc-skif-alt3 myimage\n";
    print "       $PROG -i eth0 -n tg3,bnx2 -o alt -p compute --permission <permission>\n";
    print "ALT Linux specific:\n";
    print "       $PROG [-N|--nfs-transport <udp|rdma>] ... transport method to access NFS server. Default: udp\n";
    print "       $PROG [-v|--verbose] ...    be more verbose\n";
    print "       $PROG [-d|--no-cleanup] ... do not cleanup temporary build directory\n";
    print "       $PROG [-f| --predefined-tmp[=<PATH>]] ... use predefined path for build dir. Do not auto cleanup.;\n";
    print "Where:\n";
    print "       <KERNELVER> ~ 2.6.32-std-def-alt1\n";
    print "       <otherifaces> ~ eth1,eth2,eth3\n";
    print "       <nodenetdrivers> ~ e1000e,atl1e\n";
    print "       <permission> ~ 755\n";
}

# Generic postscripts, common for all profiles
sub postscripts {

    # Copy xCAT initializing script for statelite mode
    print "$PROG: Copy xcatdsklspost postscript to $rootimg_dir/opt/xcat/\n";
    if( ! -d "$rootimg_dir/opt/xcat/") {
        mkdir "$rootimg_dir/opt/xcat/";
    }
    copy ("$installroot/postscripts/xcatdsklspost", "$rootimg_dir/opt/xcat/");
    chmod '0755', "$rootimg_dir/opt/xcat/xcatdsklspost";

    # Install services scripts
    my @services = ("xcatconsole.alt", "xcatpostinit.alt");
    foreach $service (@services) {
        unless ( -f "$installroot/postscripts/$service" ) {
            printf (STDERR "$PROG: Could not find the script $installroot/postscripts/$service.\n");
            return 1;
        }
        if ( -f "$installroot/etc/init.d/$service" ) {
            printf (STDERR "$PROG: Don't copy $service to $rootimg_dir/etc/init.d/$service. File already exist.\n");
            next;
        }
        print "$PROG: Copy $service to $rootimg_dir/etc/init.d/$service.\n";
        copy ("$installroot/postscripts/$service", "$rootimg_dir/etc/init.d/$service");
        chmod(0755,"$rootimg_dir/etc/init.d/$service");
        symlink "/etc/init.d/$service","$rootimg_dir/etc/rc0.d/K84$service";
        symlink "/etc/init.d/$service","$rootimg_dir/etc/rc1.d/K84$service";
        symlink "/etc/init.d/$service","$rootimg_dir/etc/rc2.d/S84$service";
        symlink "/etc/init.d/$service","$rootimg_dir/etc/rc3.d/S84$service";
        symlink "/etc/init.d/$service","$rootimg_dir/etc/rc4.d/S84$service";
        symlink "/etc/init.d/$service","$rootimg_dir/etc/rc5.d/S84$service";
        symlink "/etc/init.d/$service","$rootimg_dir/etc/rc6.d/K84$service";
    }

    # Name resolution
    # 1. Copy resolver
    unless ( -f "$installroot/postscripts/xcathostname" ) {
        printf (STDERR "$PROG: Could not find the script $installroot/postscripts/xcathostname.\n");
        return 1;
    }
    print "$PROG: Copy xcathostname to $rootimg_dir/bin/xcathostname.\n";
    copy ("$installroot/postscripts/xcathostname", "$rootimg_dir/bin/xcathostname");
    chmod(0755,"$rootimg_dir/bin/xcathostname");

    # 2. Use resolver in init scripts
    print "$PROG: Init scripts will use RESOLVE_HOSTNAME_COMMAND=/bin/xcathostname.\n";
    my $initcfg;
    open($initcfg,">>", "$rootimg_dir/etc/sysconfig/init");
    print $initcfg "RESOLVE_HOSTNAME_COMMAND=/bin/xcathostname\n";
    close($initcfg);

    # 3. Don't use default HOSTNAME/DOMAINNAME
    unless ( -f "$rootimg_dir/etc/sysconfig/network" )
    {
        printf (STDERR "$PROG: Could not find the config file $rootimg_dir/etc/sysconfig/network.\n");
        return 1;
    }
    print "$PROG: Comment out default HOSTNAME and DOMAINNAME in /etc/sysconfig/network.\n";
    open my $incfg, '<', "$rootimg_dir/etc/sysconfig/network";
    open my $outcfg, '>', "$rootimg_dir/etc/sysconfig/network.out";
    while (my $line = <$incfg>){
        chomp $line;
        if ( $line =~ /^\s*HOSTNAME=/ or $line =~ /^\s*DOMAINNAME=/ ) {
            $line =~ s/^/#/g;
        }
        print $outcfg "$line\n";
    }
    close $incfg;
    close $outcfg;
    move ("$rootimg_dir/etc/sysconfig/network", "$rootimg_dir/etc/sysconfig/network.xcatbackup");
    move ("$rootimg_dir/etc/sysconfig/network.out", "$rootimg_dir/etc/sysconfig/network");
}

# Unpack tarball to specified destination
# Use: tarball(tarball, destdir);
sub tarball($$)
{
    my $tarball=shift;
    my $destdir=shift;

    unless(-r "$tarball") {
        die "Can't read tarball file $tarball";
    }

    unless(-d "$destdir") {
        die "Can't find directory $destdir ";
    }

    # Cleanup $destdir
    print "$PROG: Remove previous installed netboot rootimg directory.\n";
    remove_tree( "$destdir", {keep_root => 1, error => \my $err } );

    if (@$err) {
        die "Can't recursive delete $destdir";
    }

    system("tar", "-xpf", "$tarball", "-C", "$destdir") == 0
        or die "tar: can't unpack tarball $tarball to $destdir\n";

    return;
}
