#!/usr/bin/perl

#Copyright (C) 1999-2001 by  Sbastien Chaumat <schaumat@ens-lyon.fr>
#                        and Loc 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-licences/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.

use FileHandle;

print STDERR "repli-update should only be called trough repli-install
or repli-sync\n";

#
#set default values
#
$dryrun="--dry-run";
$rsh = 'rsh';
$rcp = 'rcp';

@initscripts_exclude=qw(
bind
apache
netsolve
squid
gpm
dhcpc
dhcp
dhcp-beta
postgresql
);

use lib (".","/usr/share/replicator");
require("repli-common");
sub dosystem;
sub error;
sub check_list_var;

if(-r "$sharedir_in_miniroot/$default_rules_file") {
  require("$sharedir_in_miniroot/$default_rules_file");
} else {
  &error("no file $sharedir/$default_rules_file available\n");
}

if($modify_rules_file) {
  #    check_perms($conffile);
  require($user_rules_file);
}


sub usage {
  error "@_\nusage: repli-update --real --model <masterdir> --destdir <destdir>";
}

sub canon_arch {
 my $a = $_[0];
 chomp ($a);
 $a =~ s/i\d86/i386/;
 return $a;
}

#
#include/exclude rules are loaded in repli-common
#
#
#Sanity checks
#
#I need to change this to a "clever" default :)
sub warn_if_empty_update_rule {
  foreach $vv  ( @_ ) {
    if (@$vv) {
      if ($verbose){
	print "\@$vv=@$vv\n"
      }
    } 
    else {error("\@$vv is not defined. 
    To define an empty update rule use the syntax : 
    \@$vv=qw(\"\");
    in $user_rules_file .\n")}
  } 
}

@update_rules=qw(slash_exclude usr_exclude var_include);
warn_if_empty_update_rule @update_rules;

#
#proceed command line arguments
#
while (@ARGV) {
  $_ = shift @ARGV;
  if ($_ eq '--model') {
    @ARGV > 0 || usage "incomplete arg\n";
    $model = shift @ARGV;
  } elsif ($_ eq '--destdir') {
    @ARGV > 0 || usage "incomplete arg\n";
    $destdir = shift @ARGV;
  } elsif ($_ eq '--real') {
    $dryrun = '';
  } elsif ($_ eq '--dev') {
    $withdev = 1;
  } elsif ($_ eq '--nousr') {
    $nousr = 1;
  } elsif ($_ eq '--cfengine') {
    $cfengine = 1;
  } elsif ($_ eq '--noboot') {
    $noboot = 1;
  } elsif ($_ eq '-e') {
    @ARGV > 0 || usage "incomplete arg\n";
    $rsh = shift @ARGV;
    if ($rsh =~ m/ssh/) {
      $rcp = $rsh;
      $rcp =~ s/ssh/scp/;
    }
  } elsif ($_ eq '--exclude') {
    push @slash_exclude, shift @ARGV;
  } elsif ($_ eq '--usr_exclude') {
    push @usr_exclude, shift @ARGV;
  } elsif ($_ eq '--config') {
    @ARGV > 0 || usage;
    my $f = shift @ARGV;
    eval (scalar(`cat $f`)) or error "reading conffile $f";
  } else {
    die "usage: repli-update [--noboot ] [ --nousr ] --model <masterdir> --destdir <destdir> [-real] \n";
  }
}

check_var(qw(model destdir));

#$model =~ m,/$, or die "srcdir ($master) should end with a slash";

if ($cfengine) {
  dosystem("$rcp $model/etc/cfengine/cfengine.conf /tmp/cfengine.conf.$$");
}

#
#rsync configuration
#
$rsyncopt = "$dryrun --archive  --hard-links --sparse --whole-file --delete";
if ($verbose && !$serial) { $rsyncopt .=" -v "};

#
# "/" replication
#
#we copy everything EXCEPT @slash_exclude, files already handled by cfengine,

#ignore files already handled by cfengine
if ($cfengine) {
  $fh = new FileHandle "cfengine --dry-run -c -v --file /tmp/cfengine.conf.$$ 2>&1 |" or die "parsing cfengine.conf\n";
  @lines=();
  while ($_ = $fh->getline) { push @lines,$_; }
  $fh->close or die "terminating cfengine";
  #print "lines from cfengine @lines\n";
  #unlink("/tmp/cfengine.conf.$$");
  foreach (@lines) { 
    if (/: Checking\s+fs-object ([^\s]+)$/ ||
	/Checking copy from .*:.* to ([^\s]+)$/ || 
	/: Link \(([^\s]+)\s+->.*\) exists.$/ ||
	/: Error while trying to link ([^\s]+) ->/) {
      #    print STDERR "excluding $1\n";
      push @slash_exclude,$1;
  } else {
    #    print "line ingnored: $_";
  }
  }
}

#handle /etc/rc.d
if ($norcd) {
  push @slash_exclude,"/etc/rc?.d";
}

if ($handle_rcd) {
foreach (@initscripts_exclude) { push @slash_exclude,"/etc/rc?.d/*$_",
			   "/etc/cron.daily/$_","/etc/cron.d/$_";}
}

#handle /dev
push @slash_exclude,"/dev" unless $withdev;

#handle /boot
if (!$noboot) {
  $dryrun or -d "$destdir/boot/." or mkdir "$destdir/boot",0755 or die "cannot make $destdir/boot";
  dosystem("rsync $rsyncopt ${model}::replicator/boot/ $destdir/boot/. 2>&1");
}

#handle @slash_exclude
$excludefile = "/tmp/excl.rsync.$$";
$fh = new FileHandle $excludefile,"w" or die "opening exclude file:$!\n";;
foreach (@slash_exclude) { print $fh "- $_\n"; }
$fh->close;
#$includefile = "/tmp/incl.rsync.$$";
#$fh = new FileHandle $includefile,"w" or die "opening include file:$!\n";;
#foreach (@slash_include) { print $fh "+ $_\n"; }
#$fh->close;

chdir('/') or die "Unable to cd /";

dosystem("rsync $rsyncopt --one-file-system --exclude-from=$excludefile ${model}::replicator/ $destdir 2>&1");

#
#replication of /usr
#
#we copy everything EXCEPT @usr_exclude
-d "$destdir/usr" or mkdir "$destdir/usr",0755;

if (!$nousr) {
  my $usr_exclude = '';
  foreach (@usr_exclude) { $usr_exclude .= " --exclude '- $_' "; }
  dosystem("rsync $rsyncopt $usr_exclude  ${model}::replicator/usr/. $destdir/usr/. 2>&1");
}


#
#replication of /var
#
$dryrun or -d "$destdir/var/." or mkdir "$destdir/var",0755 or die "cannot make $destdir/var";;

#excludes possibles: /spool/squid/*    includes possibles: yp/nicknames  

#recreate var directory structure
dosystem("rsync $rsyncopt  --include '+ */' --exclude '- *' ${model}::replicator/var/. $destdir/var/.");

#copie only content of directories in @var_include
foreach (@var_include) {
  dosystem("rsync $rsyncopt ${model}::replicator/var/$_/ $destdir/var/$_ 2>&1");
}

if ($dryrun) {
print STDERR "
###################################################

repli-update was call without the --real argument.

Nothing changes on disk.

###################################################
"
}

