#!/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

#   *removed : &copy_deb_content (obsoleted by cdebootstrap) 
#
# 05-27-2005 : removed default lilo_template
#
# 08-29-2004
#   *change : potato stuffs moved into loadconf
#   *change : merged check_conf into loadconf
#
# 08-19-2004
#   *change : grubdir found using dpkg -L
#
# 08-17-2004
#   *change : is_installed moved here from repli-miniroot, now accept a $root parameter
#   *change : copy_deb_content moved here from repli-miniroot
#
# 08-09-2004
#   *change : grub dir changed for sarge and sid (SEb)
#   *add : $PMKFS and $PFSTABFS to the decription of partitions (SEb)


use File::Copy;
use Getopt::Long;
require "dialog.pl";

Getopt::Long::Configure("no_auto_abbrev");

#defaults settings
$confdir='/etc/replicator';
$sharedir='/usr/share/replicator';
$sharedir_in_miniroot=$sharedir;
$sbindir='/usr/sbin/';
$preinst="$confdir/repli-preinst";
$postinst = "$confdir/repli-postinst";
$conffile= "$confdir/replicator.conf";
$default_rules_file="update_rules.default";
$user_rules_file="$confdir/update_rules";
$model_info="model.info";
$nfstype="nfs";
$dumpkeys="dumpkeys";
$loadkeys="loadkeys";

# $lilo_template="/etc/lilo.conf";

$verbose=0;
$debiandir_local='/usr/local/debian';
$debian_mirror="http://ftp.debian.org/debian";
%known_debian_versions=("2.2", "potato", "3.0", "woody", "3.1", "sarge", "sid", "sid");



$grubdir=`dpkg -L grub | grep stage1\$ | xargs dirname`;
chomp($grubdir);

#the folowing two variables depends on the distro. For potato there is a special
#function at the end of this file

$kmap_dir='/etc/console';
$default_kmap="$kmap_dir/boottime.kmap.gz";


$mkfs="mkfs -text3";
$fs="ext3";

#@networks structure:
$NETWORK=0;
$DOMAINNAME=1;
$NETMASK=2;
$GATEWAY=3;
$BROADCAST=4;
$TARGETS=5;

#for partionning description
$PDIR = 0;
$PMIN = 1;
$PMAX = 2;
$PMKFS = 3;
$PFSTABFS = 4;
$PDEVICE = 5;
$PINIT = 6;
$PSTART = 7;
$PSIZE = 8;
$PID = 9;

$debarch=`dpkg --print-architecture 2>/dev/null`;
chomp($debarch);
#workaround for bad dpkg
if ($debarch eq "i386-none") {
  &verbose("Warning : dpkg --print-architecture returns \"i386-none\" instead of \"i386\".
Usually this means that gcc is not installed.
As this is not fatal I can continue.");
    $debarch="i386";
  };

sub error {
  #&rhs_clear;
  #because we use the dialog library we must write the message to STDERR before die.
  print STDERR "ERROR: $_[0]\n";
  die;
}

sub check_perms {
  my $file=$_[0];
  ($uid,$perms)=(stat($file))[4,2];
  ($uid==0) or &error("The file $file must be owned by root.");
  ($perms & 0033)==0 or error("The file $file must be writable/executable only by root.");
}

sub verbose {
  if ($verbose) {print STDERR "$_[0]\n"}
}

sub loadconf{
  if(-r $conffile) {
    #removed check_perm to allow running from CVS
    #    check_perms($conffile);
    &verbose("checking syntax of $conffile");
    &dosystem("perl -c $conffile");
    &verbose('ok');
    require($conffile);
  }
  else {
    &error("$conffile not readable");
  }
  $host=`uname -n`;
  chomp($host);
    $specific_conffile=$conffile."_".$host;
  if (-r $specific_conffile) {
    &verbose("found specific configuration : $specific_conffile");
    #check_perms($conffile);
    &verbose("checking syntax of $specific_conffile");
    &dosystem("perl -c $specific_conffile");
    &verbose('ok');
    require($specific_conffile);
  }
  unless ($debian_version) {
    my $file='/etc/debian_version';
    if (!-e $file){&error("The file $file is missing")}
    $current_version=`cat /etc/debian_version`;
    $debian_version=$known_debian_verions{$current_version} or $debian_version="sarge";
    &verbose("Detected debian version : $debian_version");
  }
  print STDERR "DEBUG : debian_version=$debian_version\n" if $debug;
  if ($debian_version eq "potato"){
    $kmap_dir='/etc/console-tools';
    $default_kmap="$kmap_dir/default.kmap.gz";
  }
  #some sanity checks
  my $length=@extra_keymaps;
  $length % 2 && &error("\@extra_keymaps has odd length");

  $readconf=1;
}

#
#common subs
#
sub dosystem {
  my ($package,$filename,$line) = caller;
  &verbose("exec: $_[0]");
  system($_[0]) and &error("Command failed!!! at $package, $filename, $line : $_[0]\nHINT : $_[1]");
}

sub docopy {
  &verbose("copying: $_[0] $_[1]");
  (-r $_[0]) or &error("$_[0] not readable"); 
  copy(@_) or &error("unable to copy $_[0] to $_[1]");
}

sub echo_to {
	my ($fname,$string) = @_;
	my $f = new FileHandle ">$fname" or &error("opening $fname:$!\n");
	print $f $string;
	$f->close;
}

sub check_var {
  foreach $vv  (@_) {
    if ($$vv) {
      &verbose("\$$vv=$$vv");
    }
    else {&error("\$$vv is not defined. Check $conffile")}
  }
}

sub check_list_var {
  foreach $vv  ( @_ ) {
    if (@$vv) {
      &verbose("\@$vv=@$vv");
    }
    else {&error("\@$vv is not defined. Check $conffile")}
  }
}

sub get_ip_of {
  my $hostname=$_[0];
  $ad=gethostbyname($hostname);
  $ad=join(".",unpack("C4",$ad));
  $ad or &error("cannot find IP address for $hostname.\n Check /etc/hosts or your DNS.");
  if ($ad eq "127.0.0.1") {&error("I got $ad when looking for $hostname\'s IP.\n Check /etc/hosts or your DNS.")}
  return "$ad";
}

sub use_dhcp{
  $cmdline=`cat /proc/cmdline`;
  if ($cmdline=~/ip=dhcp/){$use_dhcp=1}
  else {$use_dhcp=0}
}


sub fullname {
  return $_[1] ? "$_[0].$_[1]" : "";
}

#good_disk and find_disk are used both by repli-dialog and by repli-install

sub sizeconv {
  my $p = $_[0];
  if ($p =~ /^\s*(\d+)\s*M[boBO]$/) {
    return $1*1024*2;
  } elsif ($p =~  /^\s*(\d+)\s*K[oObB]$/) {
    return $1*2;
  } else {
    error "invalid size specification:$p: allowed = <size>Mb | <size>Kb)\n";
  }
}

#compute partitions boundaries
sub calc_part_bound {
  foreach $p (@autopart_specs) {
    if ($p->[$PMIN] ne "remaining") {
      $p->[$PMIN] = sizeconv($p->[$PMIN]);
      $p->[$PMAX] = sizeconv($p->[$PMAX]);
      $param{'minsize'} += $p->[$PMIN];
      $param{'maxsize'} += $p->[$PMAX];
    }
  }
}

# find from /proc hardisk (device with media==disk, and return capacity +geometry)
sub good_disk {
  my ($cat,$disk) = @_;
  my $fh = new FileHandle "/proc/$cat/$disk/media";
  if ($fh) {
    # skip IDE device others than CD
    my $media = $fh->getline;
    $fh->close;
    chomp $media;
    if (!($media eq 'disk')) {
      &verbose("$cat/$disk media = $media,ignored\n");
      return undef;
    }
    $fh->close;
  }
  my $capacity = `sfdisk -s /dev/$disk 2>/dev/null`;
  if ($capacity =~ /^\d+$/) {
    $capacity *= 2;
    if ($capacity < $param{'minsize'}) {
      my $mb = $capacity / 1000 /2;
      print STDERR ("$cat/$disk size = ".$mb."Mbyte(s) : disk too small (ignored)
Check the \@autopart_specs variable in $conffile.\n");
      return undef;
    }
    my $param = `sfdisk -g /dev/$disk`;
    if ($param =~ m@^/dev/$disk: (\d+) cylinders, (\d+) heads, (\d+) sectors/track$@) {
      my ($c,$h,$s) = ($1,$2,$3);
      if ($disk =~ m,^rd/c.d.$,) {
	return [ "/dev/$disk", "/dev/$disk"."p",$capacity,$c,$h,$s ];
      } else {
	return [ "/dev/$disk", "/dev/$disk", $capacity,$c,$h,$s ];
      }
    }
  }
  return undef;
}

# enumerate ide and scsi devices until finding a Hard Disk with good_disk
sub find_disk {
  if ($target_disk) {
    my $f = good_disk 'ide',$target_disk
      or good_disk 'scsi',$target_disk
	or good_disk 'rd',$target_disk;
    $f or error "cannot get info for $target_disk";
    return $f;
  }
  foreach ('a'..'f') {
    my $f = good_disk 'ide',"hd$_";
    return $f if $f;
  }
  foreach ('a'..'f') {
    my $f = good_disk 'scsi',"sd$_";
    return $f if $f;
  }
  foreach (0..3) {
    my $f = good_disk 'rd',"rd/c0d$_";
    return $f if $f;
  }
  error "cannot find Hard Disk for installation";
}

sub is_installed {
  my $package=$_[0];
  my $root=$_[1];
  $debug and print STDERR "DEBUG root parameter : $root\n";
  my $command="dpkg -s $package 2>/dev/null | grep  Status | grep -q Status.*install.*ok.*installed";
  if ($root) {
    $command = "chroot $root $command";
    my $comment = "in $root";
  } #option --root does not work in dpkg (see bug #153305)
  unless (system($command)){
    &verbose("$package is installed $comment");
    return 1;
  }
  &verbose("$package is not installed $comment");
  return undef;
}

#moving the folowing to the calling script to allow changing the conffile, especially in repli-sync
#it's also better to only put definitions here

#&loadconf;
#&check_conf;


$end=1;
