#!/usr/bin/perl
#
# Module: vyos-restore-static-ipv6.pl
#
# **** License ****
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# 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.
#
# Copyright (C) 2014 VyOS Development Group
#
# **** End License ****

use lib "/opt/vyatta/share/perl5";
use strict;
use warnings;
use POSIX;
use Fcntl;
use Sys::Syslog;
use Vyatta::Config;
use Vyatta::Interface;
use Data::Dumper;

use constant
{
    # Program settings
    PROGRAM_NAME => "vyos-intfwatchd",
    PROGRAM_VERSION => "1.0",
    PID_FILE => "/var/run/vyos-intfwatchd.pid",

    # Program exit codes
    SUCCESS => 0,
    ERROR => 1,

    # Subroutine error codes
    SUB_ERROR => 0,
    SUB_SUCCESS => 1,

    # Fcntl file lock/unlock constants
    SET_EXCLUSIVE_LOCK => 2,
    UNLOCK => 8
};

my $debug = 0;

sub daemonize
{
     syslog("info", "%s", "Starting in daemon mode");

     my $pid = fork();
     if (!defined($pid))
     {
          # Fork failed
          die "Could not spawn child process: $!, exiting";
     }
     elsif ($pid > 0)
     {
         # Child has been spawned succefully,
         # parent should terminate now
         exit(SUCCESS);
     }
     chdir("/");
     umask(0);
     setsid();

     # Close standard i/o stream descriptors
     open STDIN, "/dev/null" or die "Can't read /dev/null: $!";
     open STDOUT, ">>/dev/null" or die "Can't write to /dev/null: $!";
     open STDERR, ">>/dev/null" or die "Can't write to /dev/null: $!";
}

sub writePid
{
    my ($pid, $fh) = @_;

    unless (flock($fh, SET_EXCLUSIVE_LOCK))
    {
       syslog("err", "%s", "Could not lock PID file: $!");
       exit(ERROR);
    }

    print($fh $pid);
}

sub releasePid
{
    my $fh = shift;
    flock($fh, UNLOCK);
    close($fh);
    unlink(PID_FILE);
}


daemonize();
my $pidFile = PID_FILE;
unless (open PID_HANDLE, ">$pidFile")
{
    syslog("err", "%s", "Could not create PID file: $!");
    exit(1);
}
writePid($$, \*PID_HANDLE);

my $config = new Vyatta::Config();

my $ip_monitor = "ip monitor link";
unless (open(HANDLE, "$ip_monitor|"))
{
    syslog("err", "%s", qq{Could not start IP monitor: $!\n});
    exit(1);
}

sub terminate
{
        my $error = shift;
        syslog("notice", "%s", PROGRAM_NAME." is terminating");
        releasePid(\*PID_HANDLE);
        exit(0);
}

$SIG{'INT'} = \&terminate;
$SIG{'TERM'} = \&terminate;
$SIG{'KILL'} = sub { exit(0); };

# This solution should be bad enough to be fixed immediately
# when feasible.

while(<HANDLE>)
{
    if( $_ =~ /^[0-9]+:\s+([^@]+)(@.*)*:\s+<.*UP,.*>/ )
    {
        my $intf_name = $1;
        my $intf = new Vyatta::Interface($intf_name);
        my $intf_addr_path = $intf->path() . " address";

        # Get IPv6 addresses
        my @addresses = grep /:/, $config->returnEffectiveValues($intf_addr_path);
        print Dumper(@addresses) if $debug;

        foreach my $address (@addresses)
        {
            system("ip address add $address dev $intf_name");
            if( $? != 0 )
            {
                syslog("err", "%s", "Could not add address $address: $!");
            }
            else
            {
                syslog("notice", "%s", "Restoring address $address on interface $intf_name");
            }
        }

        $intf = undef;
    }
}