#!/usr/bin/perl
# debugnet.pl, generate a working tree of test servers to debug dancer
#
# This file is copyright (C) 2001 Andrew Suffield
#                                  <asuffield@users.sourceforge.net>
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA

use warnings;
use strict;
use Cwd;
use File::Copy;
use File::Path;
use Getopt::Std;

# This needs to be some command which executes a GNU make
my $make = 'make';

my $base_tree = cwd;
-e "$base_tree/modules/loader.c" or die "Couldn't find dancer source tree rooted at $base_tree, aborting\n";
my $build_tree = "$base_tree";
my $debug_tree = "$base_tree/debug-tree";
my $leaves = 3;
my $hubs = 2;
my $shared = 1;
my $config = <<'EOF';
# Generated by debugnet.pl

A:debugnet:debugnet:Generated by debugnet.pl
Y:1:90:0:20:1000000
# I:127.0.0.1::x::1
O:*:$1$nmNi3oKw$/TTB9SfKK3.KPYfKBYLy20:admin:abcdDfFgGhHkKlLmMnNpPrRsSUvVwWxXyYzZ0123459*:1:acdDfFgGhHkKlpRsSUvVWXyYzZ0123459
O:*:$1$nmNi3oKw$/TTB9SfKK3.KPYfKBYLy20:luser::1:
H:*::*

C:127.0.0.1:foo:services.dancer-debug::1
N:127.0.0.1:$1$uRZdBj4T$RPisyG86/WK4EgptgrtUU0:services.dancer-debug::1

EOF
my $modules_conf = <<'EOF';
load support
load database
load user
load server
load query
load admin
load extra
EOF
my $password = 'password';
my $password_hash = '$1$uRZdBj4T$RPisyG86/WK4EgptgrtUU0';
my $proxy_port = 6667;

my %paths_replace = (DPATH => '"./"',
		     SPATH => '"../ircd"',
		     MODPATH => '"../lib/dancer-ircd/"',
		     CPATH => '"ircd.conf"',
		     MCPATH => '"../modules.conf"',
		     KPATH => '"kline.conf"',
		     DLPATH => '"dline.conf"',
		     MPATH => '"motd"',
		     LPATH => '"ircd.log"',
		     PPATH => '"ircd.pid"',
		     HPATH => '"ohelp"',
		     OPATH => '"omotd"',
		     DBPATH=> '"./db"',
		    );

my %args;
my %statemap = (on => 1,
		off => 0,
	       );

getopts("chH:L:qs:", \%args);

if ($args{h})
  {
    print <<'EOF';
debugnet.pl [-c] [-H hubs] [-L leaves] [-h] [-q]
-c                 Only build config tree, not source
-h                 Show this help
-H hubs            Set the number of hubs to create (default: 2)
-L leaves          Set the number of leaf nodes to create (default: 6)
-q                 Be quiet (no output unless error)
-s [on|off]        Set shared modules on or off
EOF
    exit;
  }

$hubs = $args{H} if defined $args{H};
$leaves = $args{L} if defined $args{L};
$shared = $statemap{$args{s}} if defined $args{s};

build() unless $args{c};
create_tree();
unless($args{q})
  {
    print "Done\n";
    print "Change into directory $debug_tree\n";
    print "and run ./start to launch the ircds and start the proxy.\n";
  }

################################################################################

sub prep_source
  {
    my $pipe = '';
    $pipe = '>/dev/null' if $args{q};
    system("$build_tree/autogen.sh $pipe");
    rename "$build_tree/src/paths.c","$build_tree/src/paths.c.old"
      or die "Couldn't back up paths.c\n";
    unlink("$build_tree/src/paths.o");
    create_paths("$build_tree/src/paths.c.old", "$build_tree/src/paths.c");
  }

sub create_paths
  {
    my ($old_paths, $new_paths) = @_;
    open(my $old, "< $old_paths");
    open(my $new, "> $new_paths");
    while(my $line = <$old>)
      {
	chomp $line;
	if ($line =~ /^\#\s*define\s+(\S+)\s*/)
	  {
	    my $const = $1;
	    $line = "#define $const" . ' ' x (8 - length $const) . "$paths_replace{$const}" 
	      if exists $paths_replace{$const};
	  }
	print $new "$line\n";
      }
  }

sub restore_source
  {
    unlink "$build_tree/src/paths.c.debug";
    rename "$build_tree/src/paths.c", "$build_tree/src/paths.c.debug";
    rename "$build_tree/src/paths.c.old", "$build_tree/src/paths.c";
  }

sub build
  {
    prep_source();

    # Build the ircd
    my $pipe = '';
    $pipe = '>/dev/null' if $args{q};
    my %sharedmap = (0 => 'disable', 1 => 'enable');
    print "./configure --$sharedmap{$shared}-shared-modules --prefix=$debug_tree && $make install" unless $args{q};
    system("cd $build_tree && ./configure --$sharedmap{$shared}-shared-modules --prefix=$debug_tree $pipe && $make install $pipe") == 0
      or do {
	restore_source();
	die "Build failed, aborting\n";
      };

    restore_source();

    # Create/check the debug tree
    mkdir $debug_tree unless -e $debug_tree;
    die "The target tree $debug_tree is not a directory!\n" unless -d $debug_tree;

    symlink "$debug_tree/sbin/dancer-ircd", "$debug_tree/ircd";
  }

sub create_tree
  {
    print "Creating debug tree in $debug_tree\n" unless $args{q};
    my @names = ();
    my %port = ();
    my %hub = ();
    foreach (1..$hubs)
      {
	my $name = "hub".$_.".dancer-debug";
	push @names, $name;
	# Hubs have ports in the range 7666..7665+$hubs
	$port{$name} = 7665 + $_;
      }
    foreach (1..$leaves)
      {
	my $name = "leaf".$_.".dancer-debug";
	push @names, $name;
	# Leaves have ports in the range 8666..8665+$leaves
	$port{$name} = 8665 + $_;
	my $hub_number = int(($_ - 1) / ($leaves / $hubs));
	$hub_number++ unless $hub_number >= $hubs;
	$hub{$name} = "hub$hub_number.dancer-debug";
      }

    open(MODULESCONF, ">$debug_tree/modules.conf")
      or die "Can't create $debug_tree/modules.conf: $!\n";
    print MODULESCONF "path $debug_tree/lib/dancer-ircd/\n";
    print MODULESCONF $modules_conf;
    close MODULESCONF;

    open(PROXYCONF, ">$debug_tree/proxy.rc")
      or die "Can't create $debug_tree/proxy.rc: $!\n";

    # OK, make the trees
    foreach my $name (@names)
      {
	print "Creating files for $name\n" unless $args{q};
	# Clear all the old files
	rmtree "$debug_tree/$name" if -e "$debug_tree/$name";

	# Make the new tree
	mkdir "$debug_tree/$name";

	# Create the empty files
	foreach my $file (qw/dline.conf/)
	  {
	    open(FH, ">$debug_tree/$name/$file");
	    close(FH);
	  }

	# Create the config file
	open(CONF, ">$debug_tree/$name/ircd.conf");

	# First the stub
	print CONF $config;

	# Then the M:line
	print CONF "M:$name:127.0.0.1:dancer debug server:\n";

	# Now the C/N pairs
	foreach my $peer (@names)
	  {
	    # Skip myself
	    next if $peer eq $name;
	    # What to put in the port field in the C:line?
	    my $port = "";
	    if ($name =~ /hub/)
	      {
		$port = $proxy_port if $peer =~ /hub/;
	      }
	    else
	      {
		$port = $proxy_port if $peer eq $hub{$name};
	      }
	    print CONF "C:127.0.0.1:$password:$peer:$port:1\n";
	    print CONF "N:127.0.0.1:$password_hash:$peer\::1\n";
	    print CONF "\n";
	  }

	# And lastly the P:line
	print CONF "P::127.0.0.1::$port{$name}\n";

	close(CONF);

	# OK, write to the config for proxytee.pl
	print PROXYCONF "$name,127.0.0.1,$port{$name}\n";
      }
    close PROXYCONF;
    symlink "../tools/start_debugnet.pl", "$debug_tree/start";
    symlink "../tools/proxytee.pl", "$debug_tree/proxy";
  }
