#!/usr/bin/perl

#Copyright (C) 1999-2005 by  Sebastien Chaumat <schaumat@debian.org>
#                        and Loic Prylli <lprylli@lhpca.univ-lyon1.fr>

#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.

#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.

#    A copy of the GNU General Public License is available as
#    `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution
#    or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html.  You
#    can also obtain it by writing to the Free Software Foundation, Inc.,
#    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# changelog

# 05-30-2005
#   * change : use cdeboostrap instead of debootstrap
#   * change : remove use of copy_deb_content
#
# 02-06-2005
#   *change : install libdb4.1 instead of libdb4.0
#
# 09-01-2004
#   *change : removed dpkg-repack from the miniroot (breaks repli-miniroot) (Ferran Jorba)
#   *add: a warning about cleaning old miniroot in case of failure
#
# 08-29-2004
#   *change : &loadconf called here and no more in repli-common
#
# 08-20-2004
#   *add : export DIALOG_OPTS in /etc/init.d/replicator (nologin case)
#
# 08-19-2004
#   *change : list of allowed acces methods : add ftp:// and fixed file:///
#
# 08-17-2004
#   *add : install grub and dpkg-repack in the miniroot thus grub can be copied on the target if needed (not used yet)
#   *change : is_installed moved to repli-common, now accepts a $root parameter
#   *change : copy_deb_content moved to repli-common
#   *add : create $nfsroot/hosts using $hosts_supp, copy resolv.cont in the miniroot (suggested by Robert A Nader)
#   *add : copy /etc/apt/sources.list in the miniroot
#
# 06-03-2004
#   *fix : removed some packages from the debootstrap exclude list
#   *add : a warning about tweaking IDE disks
#
# 01-01-2004
#   *add : cleaning runlevels to speedup the boot process and remove unuseful daemons
#   *add : switching between runlevel 2 or 3 depending on the value of nologin -> need TEST
#
# 12-21-2003
#   *add : $nologin
#
# 09-20-2003
#   *fix : added libdb4.0,libgdbm3 in $dbs_include to fix perl dependencies
#   *add : install hwtools in the miniroot because this is the natural place to optimize the target HD

use FileHandle;
use File::Path;
use File::Copy;
use Getopt::Long;

require("dialog.pl");
use lib (".","/usr/share/replicator");
require("repli-common");

sub loadconf;
&loadconf;

#$debug_repli_miniroot=1;

#distribution to bootstrap

$distro=$debian_version;

#packages to add to the miniroot
@dbs_include=("sysvinit","netbase","hdparm","dialog","rsync","perl","perl-modules","reiserfsprogs","xfsprogs","jfsutils","hwtools","grub","binutils","cfengine");
$dbs_include=join(" --include ","",@dbs_include);

#scripts to remove from rc2.d and rc3.d
@useless_init_scripts=qw(exim4 inetd cron atd sysklogd klogd stop-bootlogd);

if ($verbose) {$dbs_verbose="--verbose"};

sub dosystem;
sub docopy;
sub error;
sub echo_to;
sub get_ip_of;
sub check_var;
sub is_installed;

sub usage {
  print STDERR "@_\nusage: repli-miniroot [-u/--update-config]\n\n";
  exit 0;
}

sub check_for_old_miniroot {
    if (-d "$nfsroot/usr") {
	&error("You need to clean the old miniroot ($nfsroot) before trying to recreate it.
Tip : you can keep $nfsroot/var/cache/apt/archives and its content.");
    };
}

sub ask_for_debootstrap_mirror{
    my $title="Source of packages";
    my $message=">Give me the path to your favorite Debian mirror
>(http://, ftp://, file:///). 
>
>It should contain the \"dists\" directory";
    my $width=80;
    if (rhs_inputbox($title,$message,$width,$debian_mirror))
    {
	$remote_debiandir_to_test=$dialog_result;
	mkpath(["$nfsroot"],1,0755);
	my $dbs_command="cdebootstrap $dbs_verbose --arch $debarch $dbs_include -f standard $distro $nfsroot $remote_debiandir_to_test";
	if ($debug_repli_miniroot){
	    system(&error($dbs_command));
	    exit 0;
	}
	if (system($dbs_command)){
            print "Please take note of the error and press Return to continue... ";
            $_ = <STDIN>; 
	    rhs_msgbox("Error",">It seems that cdebootstrap failed.
>Maybe the mirror you choose is invalid.
>You should try again and see if debootstrap can resume
>the installation.
>Otherwise remove
>$nfsroot
>and try another mirror.",70);
	    return undef;
	} 
	else{
	    return $remote_debiandir_to_test;
	}
    }
    else{
	exit 0;
    }
}

sub install_distro{
  $miniroot_is_installed=&ask_for_debootstrap_mirror until $miniroot_is_installed;
}


sub update_config {
  mkdir "$nfsroot/target";
  #remove hostname (it is passed on the kernel command line)
  unlink "$nfsroot/etc/hostname";
  #kernel kmap
  docopy($default_kmap,"$nfsroot/$default_kmap");
  #securetty
  echo_to "$nfsroot/etc/securetty","tty1\ntty2\ntty3\ntty4\ntty5\ntty6\nttyS0\nttyS1";

  #fstab
  print STDERR "Creating $nfsroot/etc/fstab...";
  echo_to  "$nfsroot/etc/fstab","/dev/root / nfs-root defaults 0 0\n".
    "none     /proc proc  defauts 0 0\n";
  print "done\n";

  #network
  my $f="$nfsroot/etc/network/interfaces";
  mkpath(["$nfsroot/etc/network"],1,0755);
  print STDERR "Creating $f...";
  if ($debian_version>2.9){echo_to($f,"auto lo\n")};
  echo_to("$f","iface lo inet loopback\n");
  print STDERR "done\n";

  #inittab
  if ($serial) {
    &dosystem("perl -i -lpe 's/#T0/T0/' $nfsroot/etc/inittab");
  }

  #ssh
  #  unlink("$nfsroot/etc/init.d/ssh"); #disable server
  # mkpath(["$nfsroot/root/.ssh"],1);
  #  if ($sshfile) {
  #    docopy "$sshfile","$nfsroot/root/.ssh/identity" or error "copying identity";
  #  }
  #  echo_to "$nfsroot/root/.ssh/config","Cipher =  arcfour
  #";

  #
  #preparing replicator startup at root login (runlevel2) or at the end of the rc3.d phase
  #
  #cleaning runlevel S, 2 and 3
#  &dosystem("rm -f $nfsroot/etc/rcS.d/S??bootlogd");
  foreach $script (@useless_init_scripts){
    &dosystem("rm -f $nfsroot/etc/rc[2,3,S].d/[S,K]??$script");
  }
  if ($nologin) {
    #ici edition de inittab
    #prepare to start in runlevel 3 and launch repli-dialog at the end
    #of rc3.d
    &dosystem("sed -i -e s/^id:.:initdefault/id:3:initdefault/ $nfsroot/etc/inittab");
    &echo_to("$nfsroot/etc/init.d/replicator","#!/bin/sh
export DIALOGOPTS=--no-collapse
/usr/sbin/repli-dialog
");
    &dosystem("chmod +x $nfsroot/etc/init.d/replicator");
    &dosystem("chroot $nfsroot ln -sf /etc/init.d/replicator /etc/rc3.d/S99replicator");
  } else {
    #start in runlevel 2, launch repli-dialog after root's login
    &dosystem("sed -i -e s/^id:.:initdefault/id:2:initdefault/ $nfsroot/etc/inittab");
    &echo_to("$nfsroot/root/.bashrc","
if test `tty` = '/dev/tty1' || test `tty` = '/dev/ttyS0' ; then
  export PATH=/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11
  export DIALOGOPTS=--no-collapse
  /usr/sbin/repli-dialog
fi
");
  }
#  echo_to "$nfsroot/root/.bash_profile","
#PATH=/usr/bin:/bin:/usr/sbin:/sbin/:/root:.
#test -x /usr/bin/ssh-agent && exec ssh-agent bash
#source .bashrc
#";

  #if we upgrade replicator we need to transfert new scripts into the miniroot.
  #Thus update-config also copy scripts.
  print STDERR "Installing replicator's scripts in $nfsroot...\n";
  #make the directories
  mkdir("$nfsroot/$confdir",0755);
  mkdir("$nfsroot/$sharedir_in_miniroot",0755);
  mkdir("$nfsroot/usr/sbin",0755);

  #copy shared files
  docopy("$sharedir/repli-common","$nfsroot/$sharedir_in_miniroot/repli-common");
  chmod(0644,"$nfsroot/$sharedir/repli-common");
  docopy("$sharedir/$default_rules_file","$nfsroot/$sharedir_in_miniroot/$default_rules_file");

  #copy scripts from sharedir to $nfsroot/usr/sbin
  @scripts=qw(repli-dialog repli-install);
  foreach $script (@scripts){
    my $dest="$nfsroot/usr/sbin/$script";
    docopy("$sharedir/$script",$dest);
    chmod(0744,$dest);
  }

  #copy scripts from /usr/sbin to $nfsroot/usr/sbin
  @scripts=qw(repli-update);
  foreach $script (@scripts){
    my $dest="$nfsroot/usr/sbin/$script";
    docopy("$sbindir/$script",$dest);
    chmod(0744,$dest);
  }

  print STDERR "done\n";

  print STDERR "Installing replicator\'s configuration in $nfsroot/$confdir...";
  &dosystem("cp -rv $confdir $nfsroot/etc/");
  if (!(-r $postinst)){echo_to($postinst,"#/bin/sh\n")}
   chmod(0755,"$nfsroot/$confdir/repli-postinst");
  if (!(-r $preinst)){echo_to($preinst,"#/bin/sh\n")}
  chmod(0755,"$nfsroot/$confdir/repli-preinst");
  #default model is miniroot_server
  unless ($model){$model=$miniroot_server}; #add here dump of partition of the model with sfdisk
  echo_to "$nfsroot/$confdir/$model_info","
#AUTOMATICALY CREATED BY repli-miniroot. DO NOT EDIT.
\$model=\"$model\";";
  print STDERR "done\n";
  #copy the lilo and the fstab of the miniroot_server (=model for this use)
  if ($lilo_template)
    {
      docopy($lilo_template,"$nfsroot/$confdir/lilo.conf.templ");
      chmod (0600, "$nfsroot/$confdir/lilo.conf.templ");
    }
  #  docopy("/etc/fstab","$nfsroot/root/fstab-$model"); #not used yet. Can be useful anyway.
  #in order to support copying the miniroot (for the target to become a model for a further install):
  print STDERR "(Re)Creating $nfsroot/etc/hosts...";
  my $fh = new FileHandle ">$nfsroot/etc/hosts" or error "cannot open $nfsroot/etc/host:$1\n";
  print $fh "127.0.0.1 localhost\n".
    "$miniroot_server_addr $miniroot_server ".
      fullname($miniroot_server,$miniroot_domainname)."\n";
  foreach $n (@networks) {
    my $dname = $n->[$DOMAINNAME];
    foreach $host (@{$n->[$TARGETS]}) {
      my $addr = get_ip_of($host);
      print $fh "$addr ".fullname($host,$dname)." $host\n";
    }
  }
  $hosts_supp and print $fh $hosts_supp; #suggested by Robert A Nader
  $fh->close;
  print STDERR "done\n";
  &docopy("/etc/resolv.conf","$nfsroot/etc/resolv.conf");#suggested by Robert A Nader
  &docopy("/etc/apt/sources.list","$nfsroot/etc/apt/sources.list");
}

sub set_miniroot_pwd {
 &rhs_msgbox("Setting the root password",">You will now be prompted twice for a password. 
>This is the root password you should give after booting a target 
>with the installation bootdisk.",80);
 &rhs_clear;
 while(system("chroot $nfsroot passwd")){};
}

sub mirror_one {
  my $host = $_[0];
  mkpath(["$nfsroot-$host"],1);
  &dosystem("rm -rf $nfsroot-$host/*");
  &dosystem("cp -dRl $nfsroot/. $nfsroot-$host/.");
  &dosystem("rm -rf $nfsroot-$host/var/log");
  &dosystem("cp -dRp $nfsroot/var/log $nfsroot-$host/var/.");
  &dosystem("rm -rf $nfsroot-$host/etc");
  &dosystem("cp -dRp $nfsroot/etc $nfsroot-$host/.");
}

sub mirror_tree {
  foreach $n (@networks) {
    foreach $host (@{$n->[$TARGETS]}) {
      if (@mach_patterns == 0) {
	&mirror_one($host);
      } else {
	foreach $pat (@mach_patterns) {
	  if ($host =~ m|^$pat|) {
	    &mirror_one($host);
	    break;
	  }
	}
      }
    }
  }
}

sub end_message {
  rhs_msgbox("Finished",">repli-miniroot has sucessfully created the nfsroot filesystem.
Don\'t forget to export $nfsroot for your targets
>with options: rw,no_root_squash\n",80);
  rhs_msgbox("Warning",">If you remove replicator from your system, the filesystem under
>$nfsroot 
>will not be removed automatically.
>
>You will have to delete it by hand.\n",80);
 rhs_msgbox("Notice",">If the targets use IDE disk(s), you are strongly advised to
> activate (U)DMA in $nfsroot/etc/hdparm.conf.
>
> Otherwise the replication may occurs using PIO which is very, very slow.
>\n",80);
}

sub create_initrd {
  my $initrd_size = "2000";
  my ($dir,$file) = ("/var/tmp/miniroot-tmp/mnt_initrd","/var/tmp/miniroot-tmp/initrd.img");
  mkpath(["$dir"],1,0755) or error "making dir";
  &dosystem("dd bs=1k count=2000 < /dev/zero > $file");
  &dosystem("mke2fs -F $file $initrd_size");
  &dosystem("mount $file -oloop $dir");
  mkpath(["$dir/sbin","$dir/bin","$dir/etc","$dir/dev","$dir/var/run",
	  "$dir/nfsroot","$dir/mod","$dir/proc"],1,0755) or error "making dir";
  echo_to "$dir/etc/pump.conf","device eth0 \n { \n no-dns \n } \n";
  &dosystem("mknod $dir/dev/console c 5 1");
  do_copy("$sharedir/repli-busybox","$dir/sbin/repli-busybox");
  foreach (qw(sbin/init sbin/pump bin/mount sbin/insmod)) {
    symlink("../sbin/repli-busybox","$dir/$_") or error "symlink to $dir/$_:$!";
  }
  my $mod = new FileHandle "$dir/etc/bootmod.list","w" or error "opening $dir/etc/bootmod.list:$!";
  foreach (@net_mod) {
    docopy ("$nfsroot/lib/modules/2.2.19/net/$_.o","$dir/mod/$_.o");
    print $mod "/mod/$_.o" or error "print:$!";
  }
  $mod->close or error "closing";
  &dosystem("umount $dir");
  &dosystem("gzip $file");
  docopy("$file.gz","$nfsroot/boot/initrd-miniroot");
}

#
#the real stuff
#

#sanity checks

#check if you are root
$ui=(getpwuid($<))[3];
unless ($ui eq "0"){error("You must run this script as root.")};

#check for debootstrap (useful when working from the cvs version)
&dosystem("which cdebootstrap","cdebootstrap not found");

#read command line arguments
my $fast='';
GetOptions('update-config|u' => \$fast) or usage();

#I should merge this with above
@mach_patterns=@ARGV;
$multi_install || @mach_patterns == 0 or usage();


check_var(qw(nfsroot));

#default miniroot server is the machine where replicator is installed
$miniroot_server=`uname -n`;
chomp($miniroot_server);
$miniroot_domainname=`dnsdomainname`;
chomp($miniroot_domainname);

#check if we can resolv server IP address
$miniroot_server_addr = get_ip_of($miniroot_server);

unless ($fast) {
  &check_for_old_miniroot;
  if ($debian_version eq "potato"){require("repli-miniroot-potato")}
  else {&install_distro;}
  #I should serialize here and only start a stage if previous one was ok
  #  &create_initrd;
  &update_config;
  &set_miniroot_pwd;
  if ($multi_install) {&mirror_tree;}
  &end_message;
}
else {
  &update_config ;
  if ($multi_install) {&mirror_tree};
}
