#!/usr/bin/perl -w
#
# pbmkbm, a project-builder.org utility to make boot media
#
# $Id$
#
# Copyright B. Cornec 2011
# Provided under the GPL v2

# Syntax: see at end

use strict 'vars';
use Getopt::Long qw(:config auto_abbrev no_ignore_case);
use Data::Dumper;
use English;
use File::Basename;
use File::Copy;
use ProjectBuilder::Version;
use ProjectBuilder::Base;
use ProjectBuilder::Env;
use ProjectBuilder::Conf;
use ProjectBuilder::Distribution;

# Global variables
my %opts;					# CLI Options

=pod

=head1 NAME

pbmkbm -  a project-builder.org utility to make boot media

=head1 DESCRIPTION

pbmkbm creates a bootable media (CD/DVD, USB device, Network, tape, ...)
with a minimal distribution in it, suited for building packages for example. 
It aims at supporting all distributions supported by project-builder.org 
(RHEL, RH, Fedora, OpeSUSE, SLES, Mandriva, ...)

It is inspired by work done by Jean-Marc André around the HP SSSTK and 
aim at replacing the mindi project (http://www.mondorescue.org), but 
fully integrated with project-builder.org 

pbmkbm works in different phases. The first one is to check all

pbmkbm needs to gather a certain number of components that could come 
from various sources and could be put on a different target media.
We need a kernel, an initrd/initramfs for additional modules and init script,
a root filesystem and a boot configuration file.
Kernel, modules could come either from the local installed system 
(typically for disaster recovery context) or from a kernel package of a 
given configuration.
Utilities could come from busybox, local utilities or set of packages.
The root filesystem is made with them. 
The initrd/initramfs could be made internaly or by calling dracut.
THe boot config file is generated from analysis content or provided externally.

=head1 SYNOPSIS

pbmkbm [-vhq][-t boot-media-type [-d device]][-m os-ver-arch]
[-s script][-a pkg1[,pkg2,...]] [target-dir]

pbmkbm [--verbose][--help][--man][--quiet][--type  boot-media-type [-device device]]
[--script script][--iso iso][--add pkg1,[pkg2,...]][target-dir]

=head1 OPTIONS

=over 4

=item B<-v|--verbose>

Print a brief help message and exits.

=item B<-h|--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=item B<-q|--quiet>

Do not print any output.

=item B<-s|--script script>

Name of the script you want to execute on the related VEs after the installation.
It is executed in host environment. 
You can use the chroot command to execute actions in the VE.

=item B<-a|--add pkg1[,pkg2,...]>

Additional packages to add from the distribution you want to install on the related VE 
at the end of the chroot build.

=back 

=head1 ARGUMENTS

=over 4 

=item B<target-dir>

This is the target directory under which the VE will be created. 
Created on the fly if needed. 
If none is given use the default directory hosting VE for project-builder.org 
(Cf: vepath parameter in $HOME/.pbrc)

=back 

=head1 EXAMPLE

To setup a USB boot media on the /dev/sdb device for a Fedora 12 distribution with an i386 architecture issue:

pbmkbm -t usb -d /dev/sdb -m fedora-12-i386

=head1 WEB SITES

The main Web site of the project is available at L<http://www.project-builder.org/>. 
Bug reports should be filled using the trac instance of the project at L<http://trac.project-builder.org/>.

=head1 USER MAILING LIST

Cf: L<http://www.mondorescue.org/sympa/info/pb-announce> for announces and 
L<http://www.mondorescue.org/sympa/info/pb-devel> for the development of the pb project.

=head1 CONFIGURATION FILE

Uses Project-Builder.org configuration file (/etc/pb/pb.conf or /usr/local/etc/pb/pb.conf)

=head1 AUTHORS

The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.

=head1 COPYRIGHT

Project-Builder.org is distributed under the GPL v2.0 license
described in the file C<COPYING> included with the distribution.

=cut

# ---------------------------------------------------------------------------

my ($projectbuilderver,$projectbuilderrev) = pb_version_init();
my $appname = "pbmkbm";
$ENV{'PBPROJ'} = $appname;

# Initialize the syntax string

pb_syntax_init("$appname Version $projectbuilderver-$projectbuilderrev\n");
pb_temp_init();

GetOptions("help|?|h" => \$opts{'h'}, 
	"man|m" => \$opts{'man'},
	"verbose|v+" => \$opts{'v'},
	"quiet|q" => \$opts{'q'},
	"log-files|l=s" => \$opts{'l'},
	"script|s=s" => \$opts{'s'},
	"machine|m=s" => \$opts{'m'},
	"add|a=s" => \$opts{'a'},
	"version|V=s" => \$opts{'V'},
) || pb_syntax(-1,0);

if (defined $opts{'h'}) {
	pb_syntax(0,1);
}
if (defined $opts{'man'}) {
	pb_syntax(0,2);
}
if (defined $opts{'v'}) {
	$pbdebug = $opts{'v'};
}
if (defined $opts{'q'}) {
	$pbdebug=-1;
}
if (defined $opts{'l'}) {
	open(pbLOG,"> $opts{'l'}") || die "Unable to log to $opts{'l'}: $!";
	$pbLOG = \*pbLOG;
	$pbdebug = 0  if ($pbdebug == -1);
}
pb_log_init($pbdebug, $pbLOG);

# Get VE name
$ENV{'PBV'} = $opts{'m'};
die pb_syntax(-1,1) if (not defined $ENV{'PBV'});

die "Needs to be run as root" if ($EFFECTIVE_USER_ID != 0);

#
# Initialize distribution info from pb conf file
#
pb_log(0,"Starting VE build for $ENV{'PBV'}\n");
my $pbos = pb_distro_get_context($ENV{'PBV'});

#
# Check target dir
# Create if not existent and use default if none given
#
pb_env_init_pbrc(); # to get content of HOME/.pbrc
my $vepath = shift @ARGV;

#
# Check for command requirements
#
my ($req,$opt) = pb_conf_get_if("mkbmcmd","mkbmcmdopt");
pb_check_requirements($req,$opt,$appname);

if (not defined $vepath) {
	my ($vestdpath) = pb_conf_get("vepath");
	$vepath = "$vestdpath->{'default'}/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}" if (defined $vestdpath->{'default'});
}

die pb_log(0,"No target-dir specified and no default vepath found in $ENV{'PBETC'}\n") if (not defined $vepath);

pb_mkdir_p($vepath) if (! -d $vepath);

#
# Get the package list to download, store them in a cache directory
#
my ($mkbmcachedir) = pb_conf_get_if("mkbmcachedir");
my ($pkgs,$mirror) = pb_distro_get_param($pbos,pb_conf_get("mkbmmindep","mkbmmirrorsrv"));

my $cachedir = "/var/cache/pbmkbm";
$cachedir = $mkbmcachedir->{'default'} if (defined $mkbmcachedir->{'default'});

# Point to the right subdir and create it if needed
$cachedir .= "/$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}";
pb_mkdir_p($cachedir) if (! -d $cachedir);

#
# /proc needed
#
pb_system("mount -o bind /proc $vepath/proc","Mounting /proc");

# Installed additional packages we were asked to
if (defined $opts{'a'}) {
	$opts{'a'} =~ s/,/ /g;
	pb_system("chroot $vepath /bin/bash -c \"$pbos->{'install'} $opts{'a'} \"","Adding packages to OS by running $pbos->{'install'} $opts{'a'}");
}

#
# Clean up
#
pb_log(1,"Cleaning up\n");
pb_system("umount $vepath/proc","Unmounting /proc");

# Executes post-install step if asked for
if ($opts{'s'}) {
	pb_system("$opts{'s'} $vepath","Executing the post-install script: $opts{'s'} $vepath");
}
