#!/usr/bin/perl

#
# Update FAI mechanism
#
# this script should check local fai-classes and run update scripts
# looking for dates
#
# Copyright (C) 2007 Jan Krcmar <honza801@students.zcu.cz>
#

# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


use strict;
use Config::Auto;
use Switch;
use File::Copy;
use Fcntl;

# where the config file is stored
my $config_file = "/etc/fai-update.conf";

# mechanism configuration
my %config;
# local fai classes
my @classes;
# local fai variables
my %variables;
# connection type
my $conntype;
# path to the fai update dir
my $path;
# fai directory (where to mount)
my $faipath;
# date
my $date;
my $lastdate;



# reads config and returns log directory
sub parse_config_file {
    my @config_arr = Config::Auto::parse($config_file, format => "equal");
    my @params = ("fai_dir","update_dir","fai_afs_target","fai_nfs_target","force_afs","last_update","log","executed_scripts");
    for my $option (keys %{$config_arr[0]}) {
	switch ($option) {
	    case [@params] {
	    
	    }
	    else {
		printf LOG "Interrupted by error:\n";
		printf LOG "Error parsing config file!\nOption \"$option\" not supported.\n";
		close (LOG);
		print STDERR "Error parsing config file!\nOption \"$option\" not supported.\n";
		exit 1;
	    }
	}
    }
    %config = %{$config_arr[0]};
    foreach my $prm (@params) {
	if (!$config{$prm}) {
		printf LOG "Interrupted by error:\n";
		printf LOG "Error parsing config file!\nOption \"$prm\" is missing.\n";
		close (LOG);
		print STDERR "Error parsing config file!\nOption \"$prm\" is missing.\n";
		exit 1;
	}	
    }
}

# reads classes
sub init_variables {
    my $location;
    # get classes
    $location = $config{'fai_dir'};
    open(FILE, $location."/FAI_CLASSES");
    @classes = <FILE>;
    close(FILE);
    
    # get vars
    my @var_arr;
    if (-f "$location/variables.log") {
    	@var_arr = Config::Auto::parse($location."/variables.log", format => "equal");
	%variables = %{$var_arr[0]};
    }
    else{
    	print LOG "$location/variables.log was not found";
    	die "$location/variables.log was not found";
    }
    # get last update
    $lastdate = `cat $config{'last_update'}`;
    # get current date
    $date = `date +%y%m%d`;
    chomp($date);
}

# recognize connection type
# 1.afs 2.nfs
sub check_conn_type {
    # if forced afs loop while not mounted... this should be tested
    if ($config{'force_afs'} =~ /yes/) {
    	$conntype = "afs";
	while ( ! `cat /proc/mounts | grep -i afs` ) { 
		print "AFS not found, sleeping for a while before next try.\n";
		`sleep 2s`;
	}
	return;
    }
    # ...otherwise check conneciton
    my $in;
    $in = `cat /proc/mounts | grep -i afs`;
    if ($in) {
	$conntype = "afs";
    }
    else {
	$in = `modprobe nfs`;
	if ($in eq "") {
	    $conntype = "nfs";
	}
    else {
	printf LOG "Interrupted by error:\n";
	printf LOG "Error: unsupported connection type.\n";
	close (LOG);
	print STDERR "Error: unsupported connection type.";
	exit 1;
    }}
}

# initializes variables
sub init {
    # get the variables
    parse_config_file;
    
    # start logging
    if ( -e $config{'log'} ) {
	`mv $config{'log'} $config{'log'}.old`;
    }
    open(LOG, ">$config{'log'}");
    printf LOG "FAI update mechanism started.\n";
    printf LOG "Config file parsed succesfully.\n";
    init_variables;
    printf LOG "Date: $date\n";
    printf LOG "Last update: $lastdate\n";
    printf LOG "FAI variables and classes read succesfully.\n";
    check_conn_type;    
    printf LOG "Connection type: $conntype.\n";
    
    # start bloody portmap if nfs
    switch ($conntype) {
	case "afs" {
	    $faipath = $config{'fai_afs_target'};
	}
	case "nfs" {
    	    if (! -f "/etc/init.d/portmap") {
	    	print "Portmap not installed correctly.\n";
		printf LOG "Portmap not installed correctly.\n";
		exit;
	    }
	    print `/etc/init.d/portmap start`;
	    printf LOG "Portmap started.\n";
	    print `mount $variables{'FAI_LOCATION'} $config{'fai_target'} -t nfs`;
	    printf LOG "NFS mounted.\n";
	    $faipath = $config{'fai_nfs_target'};
	}
    }
    $path = "$faipath/$config{'update_dir'}/";
}

# call this to the end of the script
sub finish {
    `echo $date > $config{'last_update'}`;
    #unmount nfs
    if ($conntype eq "nfs") {
	`umount $config{'fai_target'}`;
	printf LOG "NFS unmounted.\n";
    }
    close (LOG);
}

# check if the file contains line
sub file_contains {
    my $patt = shift;
    chomp($patt);
    my @file = @_;
    my $found = "";
    for (my $i = 0; not($found) && ($i < @file); $i++) {
        my $line = $file[$i];
        chomp($line);
        #print "$line ... $patt\n";
	if ($line =~ /$patt/) {$found = "yes"};
    }
    return $found;
}

# go throw all the scripts
sub gothrowscripts {
    # init executed scripts file & bse variables
    my $check_es;
    if ($config{'executed_scripts'}) {
        $check_es = "yes";
	`touch $config{'executed_scripts'}`;
    }
    my $count = 0;
    print "prefix: $path\n";
    foreach my $dir (@classes) {
	chomp($dir); # ugly end of line 8-)
	open(DIR, $path.$dir);
	if (-d DIR) { # directory exists
	    my @scripts = `ls $path$dir`;	# list all scripts
	    foreach my $script (@scripts) {
		my $sdate = substr($script,0,6);
		open(ESFILE, $config{'executed_scripts'});	
		my @file = <ESFILE>;
		close(ESFILE);
		if ((($lastdate < $sdate) && ($check_es) && (!file_contains("$dir.$script", @file))) ||
		   (($lastdate < $sdate) && (!$check_es))) {
		    printf LOG "Running: ";
		    # get time
		    my $time = time();
		    # run
		    my @in = `faisource=$faipath $path/$dir/$script`;
		    $time = time() - $time;
		    # put it to log
		    print LOG @in;
		    # put script into executed file if needed
		    if ($check_es) {
		        close(ESFILE);
		        open(ESFILE, ">>$config{'executed_scripts'}");
		        print ESFILE "$path/$dir/$script";
		        close(ESFILE);
		        open(ESFILE, "$config{'executed_scripts'}");
		    }
		    # show us what have you done
		    print "$dir/$script";
		    print @in;
		    printf LOG "OK\n";
		    printf LOG "Elapsed time: $time\n";
		    $count++;
		}
	    }
	}
    	close(DIR);
    }
    close(ESFILE);
    printf LOG "Total executed script count: $count\n";
}



# little help
if ($ARGV[0] =~ /-h/) {
    print << "EOF";
Update FAI mechanism
author: Jan Krcmar <honza801\@students.zcu.cz>, GNU/GPL, 2007

this script should check local fai-classes and run update scripts

Usage: $0 [-h]
-h	prints this screen
config file is put in "$config_file"
EOF
}

# start
init;
gothrowscripts;
finish;

