#!/usr/bin/perl -w

use strict;
use XML::LibXML;
use Digest::MD5 qw(md5_hex);
use POSIX qw(strftime);
use Fcntl qw(:DEFAULT :flock);
use File::Glob ':glob';
use File::Basename;

$| = 1;

if ($> == 0) {
	exec "su", "ganymede", "-c", $0;
	exit 1;
}

my $parser = XML::LibXML->new();

my $xml_source = '/var/cache/xsltdai/ganymede-data.xml';
my $xml_dai    = '/var/cache/xsltdai/dai.xml';
my $xml_checksums = '/var/lib/xsltdai/checksums.xml';
my $xsl_dai    = '/usr/share/xsltdai/create-dai.xsl';
my $acldir     = '/var/cache/xsltdai/arp-acls';
my $lockfile   = '/var/lock/xsltdai';

open(LOCKFILE, '>', $lockfile) or die "$0: can't open lockfile: $lockfile\n";
if (flock(LOCKFILE, LOCK_EX) == -1) {
	die "$0: can't get lock on $lockfile\n";
}

my $start;
printf "dumping ganymede data..."; $start = time;
system('/etc/xsltdai/dump-data');
printf " %ds\n", time - $start;
printf "generating arp-acls..."; $start = time;
system('xalan', -xsl => $xsl_dai, -in => $xml_source, -out => $xml_dai);
printf " %ds\n", time - $start;

my $tree = $parser->parse_file($xml_dai);
my $checksums;
if (-e $xml_checksums) {
	$checksums = $parser->parse_file($xml_checksums);
} else {
	$checksums = $parser->parse_string(<<EOF);
<?xml version="1.0" encoding="UTF-8"?>
<dai>
</dai>
EOF
}
my $root = $tree->getDocumentElement();

my @acls = $root->findnodes('/dai/acl');

mkdir $acldir unless -e $acldir;
my @changed;

foreach my $acl_el (@acls) {
	my $acl_name = $acl_el->findvalue('@id');
	my $acl_md5 = md5_hex($acl_el->textContent());
	
	my $ref_checksum_el = ($checksums->getDocumentElement()->findnodes("/dai/acl[\@id = '$acl_name']"))[0];
	unless (defined($ref_checksum_el)) {
		$ref_checksum_el = $checksums->createElement('acl');
		$ref_checksum_el->setAttribute('md5sum', '');
		$ref_checksum_el->setAttribute('id', $acl_name);
		($checksums->getDocumentElement()->findnodes("/dai"))[0]->appendChild($ref_checksum_el);
	}
	
	my $ref_md5 = $ref_checksum_el->getAttribute('md5sum');
	my $changed = $ref_md5 ne $acl_md5 ? 1 : 0;
	#printf "zone: %-40s %s\n", $acl_name, ($changed ? "changed!" : "not changed");
	next unless $changed;
	printf "changed arp-acl: %s\n", $acl_name;
	
	$ref_checksum_el->setAttribute('md5sum', $acl_md5);
	push @changed, $acl_name;
	
	open(OUT, '>', $acldir."/$acl_name");
	print OUT $acl_el->textContent;
	close(OUT);
}

# delete old files
foreach my $aclfile (bsd_glob("$acldir/*")) {
	my $acl = basename($aclfile);
	next if $root->findnodes("/dai/acl[\@id = '$acl']");
	printf "deleting old acl file %s\n", $acl;
	unlink $aclfile;
}
# delete old checksums
my @checksums_to_delete;
foreach my $checksum_el ($checksums->getDocumentElement()->findnodes("/dai/acl")) {
	my $acl = $checksum_el->findvalue('@id');
	next if $root->findnodes("/dai/acl[\@id = '$acl']");
	push @checksums_to_delete, $checksum_el;
}
foreach my $checksum_el (@checksums_to_delete) {
	($checksums->getDocumentElement()->findnodes("/dai"))[0]->removeChild($checksum_el->previousSibling());
	($checksums->getDocumentElement()->findnodes("/dai"))[0]->removeChild($checksum_el);
}

$checksums->toFile($xml_checksums, 1) if (@changed > 0 or @checksums_to_delete > 0);

system("/etc/xsltdai/sync-arp-acls", @changed) if (@changed > 0);

flock(LOCKFILE, LOCK_UN);

exit 0;
1;
