#!/usr/bin/perl

use strict;

use File::Copy;

use Conf;
use Errors;
use NamedConf;
use ZoneEntry;
use ZoneFile;

BEGIN {
    @Conf::Ids = (@Conf::Ids, '$Header: /afs/sipb/project/zoned/src/RCS/zoned.pl,v 1.7 2002/01/22 23:35:05 ingolia Exp $ ');
};

eval {
  Conf::ensure_basedir();
    
    my $dirty = 0;
    
    my @zones = ZoneEntry->read_entries_file($Conf::zonelist);
    
    $dirty += regen_named_conf([@zones]);
    
    foreach my $ze (@zones) {
	if ($ze->is_master()) {
	    my $dirty_zone;
	    
	    eval {
		$dirty_zone = regen_zone($ze);
	    } ;
	    
	    if ($@) {
	      Errors::notify("Error generating zone file for " . $ze->zone()
			     . ":\n$@", 
			     "$Conf::zone_err_mailto " . $ze->maintainer(),
			     $Conf::zone_err_zephyrto,
			     'Error for ' . $ze->zone());
	    } elsif ($dirty_zone) {
		$dirty++;
		
	      Errors::notify('Zone file for ' . $ze->zone() . 
			     " reloaded.\n",
			     "$Conf::zone_reload_mailto " 
			     . $ze->maintainer(),
			     $Conf::zone_reload_zephyrto, 
			     $ze->zone() . ' reloaded');
	    }
	}
    }
    
    if ($dirty) {
	hup_named();
    }
} ;

if ($@) {
  Errors::notify(join("\n", 'Fatal error running zoned: ', $@, @Conf::Ids), 
		 $Conf::fatal_mailto, $Conf::fatal_zephyrto, 
		 'Fatal zoned error');
}

exit(0);

# Generate a new named.conf if necessary
# Return 1 if named.conf regenerated, or 0 otherwise
sub regen_named_conf {
    my $zonesref = shift;

    if (needs_update($Conf::named_conf, $Conf::named_manual, 
		     $Conf::zonelist)) {
	safe_replace($Conf::named_conf, NamedConf::named_conf($zonesref));
	return 1;
    }

    return 0;
}

# Generate a new zone file for a mastered domain if necessary
# Return 1 if zone file regenerated, or 0 otherwise
sub regen_zone {
    my $ze = shift;

    if (needs_update(Conf::filepath($ze->zonefile()), $ze->srcfile())) {
	safe_replace(Conf::filepath($ze->zonefile()), 
		     ZoneFile::get_zone_file($ze));
	return 1;
    }

    return 0;
}

sub safe_replace {
    my $filename = shift;
    my $newcontents = shift;

    if (-e $filename) {
	copy($filename, "$filename.old")
	    or die("Could not back up $filename\n");
    }
    
    open(FILE, ">$filename.$$")
	or die("Could not open $filename.$$: $!\n");
    
    print FILE $newcontents
	or die("Could not write to $filename.$$: $!\n");
    
    close(FILE) 
	or die("Could not finish writing to $filename.$$: $!\n");
    
    rename("$filename.$$", $filename)
	or die("Could not rename in $filename: $!\n");
}

# Send named a HUP signal, forcing it to re-read and reload everything
sub hup_named {
    open(PIDFILE, "$Conf::named_pidfile")
	or die("Could not open $Conf::named_pidfile", $!);

    my $pid = <PIDFILE>;
    chop $pid;

    close(PIDFILE);

    if (kill('HUP', $pid) != 1) {
	die("Could not send SIGHUP to named pid $pid: $!\n");
    }
}

# This function determines whether a generated file needs to be rebuilt 
#   based on its dependencies on other files.
# The first file is the file to be checked.  If it does not exist or has 
#   a last modification time older than the newest depended-on file, it is 
#   considered to need rebuilding and true is returned.
# If a depended-on file does not exist, a warning is issued and false
#   is returned.  This is epistatic to anything about the generated file.
sub needs_update {
    my $genfile = shift; # Generated file

    my $latest = 0;

    foreach my $depfile (@_) {
	my @depst;
	if (!(@depst = stat($depfile))) {
	    die("Could not stat depended-on file $depfile: $!\n");
	}

	if ($latest < $depst[9]) {
	    $latest = $depst[9];
	}
    }

    my @genst;
    if (!(@genst = stat($genfile))) {
	return 1;
    }

    return $genst[9] < $latest;
}









