#!/usr/bin/perl
#
# scass2bind - Perl script to read the DNS data from the SCASS tables and
# generate bind zonefiles (and named.conf) from it.
#
# usage: scass2bind [-t|-f] [zone_name[,zone_name]]
#         -t => Test mode, return number of zones needing dump, test either
#               zone_names given as commandline argument or all if none given
#         -f => Force dump, dumps either list of zones given on the command
#               line or all if none given. Returns number of zones dumped
#         Normally, checks the zones listed on the commandline and dumps them
#         if they have the do_commit flag set. Otherwise checks all zones and
#         dumps them if the have the do_commit flag set.
#
#         All dumped zones have their do_commit flag set to false and their
#         last_dump timestamp set to current_timestamp.
#
# Author: Alexander Schreiber <als@thangorodrim.de>
#
# Version: $Id: scass2zone,v 1.11 2002/09/15 22:56:55 als Exp $
#
#
#
# scass.bes - The backendsystem of SCASS (System Configuration and
#             Administration Support System)
#
# Copyright (C) 2002  Alexander Schreiber <als@thangorodrim.de>
#
# 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.
#

require("../common/load_config.pl");
require("../common/load_config_db.pl");
require("../common/db_connect.pl");

use DBI;

use strict;


sub find_zones_for_commit {
# find zones flagged for commit
# parameters: $dbh = database handle
# returns: list of id of zones flagged for commit, may be empty

    my $dbh = shift;

    my $sql;
    my $sth;
    my $data;
    my @zone_list;
    my %config;

    $sql = "SELECT id from dns_zone where do_commit = true";

    $sth = $dbh->prepare($sql);
    $sth->execute;
    while ( $data = $sth->fetchrow_hashref ) {
        unshift(@zone_list, $data->{id});
    }

    return @zone_list;
}


sub load_modules {
# loads any additionally modules found in $SCASS.BESBase/DNS/modules
# and $SCASS.BESBase/common (only the *.pm files!)
# parameters: $dbh -> database handle
# returns: 0 on success, -1 on failure

    my $dbh = shift;
 
    my $modules_dir;
    my $module_name;
    my $dir_entry;
    my %config;

    %config = &get_config_from_db($dbh);

    $modules_dir = "$config{'SCASS.BESBase'}/DNS/modules";
    unless ( -d $modules_dir ) {
        print STDERR "Modules directory $modules_dir does not exist!\n";
        return -1;
    }
    opendir(MOD_DIR, $modules_dir) or die "failed to opendir($modules_dir)";
#    print "loading modules: ";
    while ( $dir_entry = readdir(MOD_DIR) ) {
        $module_name = "$modules_dir/$dir_entry";
        if ( ( -f $module_name ) and $module_name =~ /\.pm$/ ) {
#            print "$dir_entry ";
            require($module_name);
        }
    }
    closedir(MOD_DIR);

    $modules_dir = "$config{'SCASS.BESBase'}/common";
    unless ( -d $modules_dir ) {
#        print STDERR "Modules directory $modules_dir does not exist!\n";
        return -1;
    }
    opendir(MOD_DIR, $modules_dir) or die "failed to opendir($modules_dir)";
    while ( $dir_entry = readdir(MOD_DIR) ) {
        $module_name = "$modules_dir/$dir_entry";
        if ( ( -f $module_name ) and $module_name =~ /\.pm$/ ) {
#            print "$dir_entry ";
            require($module_name);
        }
    }
    closedir(MOD_DIR);

#    print " [done].\n";

    return 0;
}

sub dump_zone_file {
# generate zone files from database
# parameters: $dbh -> database handle, $zone_id -> (database) id of zone,
#             $zone_type -> type of zone
# returns: (0, '') in case of success, (-1, $message) in case of trouble

    my $dbh = shift;
    my $zone_id = shift;
    my $zone_type = shift;

    my $handler;
    my $handler_module;
    my %config;

    my $handled = 0;

    if ( $zone_type eq '' ) {
        print STDERR "no zone type specified, skipping\n";
        return;
    }

    %config = &get_config_from_db($dbh);

    if ( $zone_type eq 'bind' ) {
        &dump_zonefile_bind($dbh, $zone_id);
        $handled++;
    }

    if ( $handled > 1 ) {
        print STDERR "internal fuckup, zone handled more than once!\n";
        return (-1, "zone handled more than once");
    }

    if ( $handled == 0 ) {
        print STDERR "no valid handler for zone_type found!\n";
        return (-1, "no handler found");
    }

    return (0, '');
}


# ##MAIN##

my $dsn;
my $dbh;
my @zone_list;
my $zone;
my $result;
my $message;
my (%config, %db_access);
my $work;
my $test_only = 0;
my @test_zones;
my $test_zone;
my $data;
my $zones_found;
my $sql;
my $sth;
my $force_dump = 0;
my @dump_zones;
my $zones_dumped = 0;
my @dumped_zones;


%db_access = &load_config();

$dsn = "dbi:Pg:dbname=$db_access{DBName} host=$db_access{'DBHost'}";
$dbh = &db_connect($dsn, $db_access{'DBUser'}, $db_access{'DBPassword'});

$dbh->begin_work or die "Failed to setup transaction!\n";

&load_modules($dbh);

%config = &get_config_from_db($dbh);


if ( scalar(@ARGV) > 0 ) {
# option processing
    if ( $ARGV[0] eq '-t' ) { # set test mode
        $test_only = 1;
        foreach $work ( @ARGV ) {
            if ( ($work ne '-t') and ( $work ne '-f') ) {
                push(@test_zones, $work);
            }
        }
    }
    
    if ( $ARGV[0] eq '-f' ) { # set forced dump mode
        $force_dump = 1;
    }
}

foreach $work ( @ARGV ) {
    if ( ($work ne '-t') and ( $work ne '-f') ) {
        push(@dump_zones, $work);
    }
}

@zone_list = &find_zones_for_commit($dbh);

# handle test mode
if ( $test_only ) {
    if ( @test_zones == () ) { # no zones to test explicitly
        $work = scalar(@zone_list);
        $dbh->commit or die "Failed to commit transaction!\n";
        $dbh->disconnect;
        exit($work);  # return number of zones found
    } else { # test for explicitly named zones
        $zones_found = 0;
        foreach $test_zone ( @test_zones ) {
            $sql = "select id from dns_zone where do_commit = true ";
            $sql .= "and zone_name = '$test_zone'";
            $sth = $dbh->prepare($sql);
            $sth->execute;
            $work = 0;
            while ( $data = $sth->fetchrow_hashref ) {
                $work++;
            }
            if ( $work != 0 ) {
                $zones_found++;
            }

        }
        $dbh->commit or die "Failed to commit transaction!\n";
        $dbh->disconnect;
        exit($zones_found);
    }
}

# handle force mode
if ( $force_dump == 1 )  {
    $zones_dumped = 0;
    if ( @dump_zones == () ) { # no zones explicitly listed, dump all
        $sql = "select id from dns_zone";
        $sth = $dbh->prepare($sql);
        $sth->execute;
        while ( $data = $sth->fetchrow_hashref ) {
            $zone = $data->{id};
            ($result, $message) = &dump_zone_file($dbh, $zone, 
                                                  $config{'DNSType'});
            unshift(@dumped_zones, $zone);
            if ( $result == -1 ) {
                print STDERR "dumping zone failed: id = |$zone|, ";
                print STDERR "message = |$message|\n";
            } else {
                $zones_dumped++;
            }
        }
    } else { # dump only the specified zones
        foreach $work ( @dump_zones ) {
            $sql = "select id from dns_zone where zone_name = '$work'";
            $sth = $dbh->prepare($sql);
            $sth->execute;
            while ( $data = $sth->fetchrow_hashref ) {
                $zone = $data->{id};
                ($result, $message) = &dump_zone_file($dbh, $zone, 
                                                      $config{'DNSType'});
                unshift(@dumped_zones, $zone);
                if ( $result == -1 ) {
                    print STDERR "dumping zone failed: id = |$zone|, ";
                    print STDERR "message = |$message|\n";
                } else {
                    $zones_dumped++;
                }
            }
        }
    }

    ($result, $message) = &write_named_config_include($dbh, @dumped_zones);
    if ( $result != 0 ) {
        print STDERR "error: $message\n";
    }
    ($result, $message) = &reload_bind($dbh);
    if ( $result != 0 ) { 
        print STDERR "error: $message\n";
    }

    $dbh->commit or die "Failed to commit transaction!\n";
    $dbh->disconnect;
    exit($zones_dumped);
}

# Still here? So we are doing normal operations
#

if ( scalar(@dump_zones) != 0 ) { # process explicitly given list
    foreach $work ( @dump_zones ) {
        $sql = "select id from dns_zone where zone_name = '$work'";
        $sql .= " and do_commit = true";
        $sth = $dbh->prepare($sql);
        $sth->execute;
        while ( $data = $sth->fetchrow_hashref ) {
            $zone = $data->{id};
            ($result, $message) = &dump_zone_file($dbh, $zone,
                                                  $config{'DNSType'});
            unshift(@dumped_zones, $zone);
            if ( $result == -1 ) {
                print STDERR "dumping zone failed: id = |$zone|, ";
                print STDERR "message = |$message|\n";
            } else {
                $zones_dumped++;
            }
        }
    }
} else { # none given, process all
    foreach $zone ( @zone_list ) {
        ($result, $message) = &dump_zone_file($dbh, $zone, $config{'DNSType'});
        unshift(@dumped_zones, $zone);
        if ( $result == -1 ) {
            print STDERR "dumping zone failed: id = |$zone|, ";
            print STDERR "message = |$message|\n";
        } else {
            $zones_dumped++;
        }
    }
}

if ( $zones_dumped != 0 ) {
    ($result, $message) = &write_named_config_include($dbh, 
                                                      @dumped_zones);
    if ( $result != 0 ) {
        print STDERR "error: $message\n";
    }
    ($result, $message) = &reload_bind($dbh);
    if ( $result != 0 ) {
        print STDERR "error: $message\n";
    }
}


$dbh->commit or die "Failed to commit transaction!\n";
$dbh->disconnect;


exit($zones_dumped);


