#!/usr/bin/perl -w

#
# snfsfstab 0.0
#
# Copyright (C)  2002 Patrice Dumas <dumas@centre-cired.fr>
#
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.

require 5;

use strict;
use FileHandle;
use SNFS;

sub parse_filesystems_file($);
sub get_hosts_mountprogs ();
sub rewrite_fstab();

sub usage()
{
	print STDERR "Usage: $0\n";
	exit 1;
}

if ($#ARGV >= 0)
{
	usage;
}

my @filesystems;
my %hosts_mountprogs = ();

my $ref_hosts_mountprogs = &SNFS::get_hosts_mountprogs;

if (defined ($ref_hosts_mountprogs))
{
	%hosts_mountprogs = %{ $ref_hosts_mountprogs };
}

my $all = 0;
@filesystems = parse_filesystems_file(\$all);

rewrite_fstab;

#my $key;
#foreach $key (keys(%hosts_mountprogs)){print "$key->$hosts_mountprogs{$key}\n";}  
#print "(@filesystems) all:$all\n";


#parse a file. If there is ALL in the file the ref passed in arg is set to 1.
#otherwise return the lines which match host:filesystem
sub parse_filesystems_file ($)
{
	my $ref_all = shift;
	my ($fs_fh, @filesystems);
	unless ($fs_fh = new FileHandle SNFS::FS_PATH, 'r')
	{
		$$ref_all = 1;
		return undef;
	}
	while (my $line = <$fs_fh>)
	{
		if ($line  =~ m/^\s*#/)
		{
			next;
		}
		elsif ($line =~ m/^\s*ALL\s*$/)
		{
			$$ref_all=1;
			return undef;
		}
		elsif ($line =~ m%^\s*([\w\-.]+):((/[\w\-.]*)+/?)$%)
		{
			push @filesystems, "$1:$2";
		}
	}
	$$ref_all = 0;
	return @filesystems;
}

	
sub rewrite_fstab()
{
	my ($fstab_fh, $fstab_snfs_fh, $hostname);
	
	unless ($fstab_fh = new FileHandle SNFS::FSTAB_PATH, 'r')
	{
		print STDERR "$0: @{[SNFS::FSTAB_PATH]}: $!\n";
		return undef;
	}
	unless ($fstab_snfs_fh = new FileHandle SNFS::FSTAB_PATH . ".snfs", '>')
	{
		print STDERR "$0: @{[SNFS::FSTAB_PATH]}.snfs: $!\n";
		return undef;
	}

	$hostname =  qx/ @{[SNFS::HOSTNAME_PATH]} / ;
	unless($? eq 0)
	{ 
		print STDERR "$0: @{[SNFS::HOSTNAME_PATH]}: $!\n";
		return undef;
	}	
	chomp $hostname;

	while (my $line = <$fstab_fh>)
	{
		my $original_line = $line;
		if ($line =~ m/^\s*#/)
		{
			print $fstab_snfs_fh $line;
		}
		elsif ($line =~ m%^\s*([\w.\-]+):((/[\w.\-]*)+/?)\s+((/[\w.\-]*)+/?)\s+nfs\s+.*\bmountprog=(\d+)\b%)
		{
			print $fstab_snfs_fh $line;
		}
		elsif ($line =~ s%^(\s*)([\w.\-]+):((/[\w.\-]*)+/?)(\s+)((/[\w.\-]*)+/?)(\s+nfs)%%)
		{
			my ($space0, $server, $server_path, $space1, $path, $nfs_field) = 
			    ($1, $2, $3, $5, $6, $8);
			if (($all == 0) and (not grep /^$server:$server_path$/, @filesystems))
			{
				print $fstab_snfs_fh $original_line;
				next;
			}
			my ($space2, $options, $remaining) = ( " ", "", "");
			if ($line =~ m%^(\s+)(\d.*)$%)
			{
				($space2, $remaining ) =  ($1, $2);
			}
			elsif ($line =~ m%^(\s+)([a-zA-Z][^\s]*)(.*)$%)
			{
				($space2, $options, $remaining) = ($1, $2, $3);
			}
			else 
			{
				$line =~ m/^.*$/;
				$remaining = $&;
			}
			my $mountprog;
			if ($mountprog = $hosts_mountprogs{$server})
			{
				my $host = check_hosts_mountprogs($mountprog, \%hosts_mountprogs);
				if ($host ne $server)
				{
					die "bug: $host from check_hosts_mountprogs($mountprog) different that direct ref in %hosts_mountprogs";
				} 
			}
			else
			{
				my $snfshost = SNFS::ADDHOST_PATH . " --tmpl-file --keep $server";
				my $host_mountprog = `$snfshost`;
				my $snfshost_exit = $? >> 8;
				if (($snfshost_exit != 0) or (!defined($host_mountprog)) or !$host_mountprog)
				{
					my $why = "exit code $snfshost_exit\n";
					if ($snfshost_exit == SNFS::CONFIG_CONFLICT)
					{
						$why = "config files conflicting\n";
					}
					elsif ($snfshost_exit == 1)
					{
						$why = "exit code 1\n";
					}
					print "$0: Error with $snfshost for $server: $why";
					next;
				}
				chomp $host_mountprog;
				my ($conf_host, $file);
				($conf_host, $mountprog,$file) = split /:/, $host_mountprog;
				if (!defined($conf_host) or !$conf_host or ($conf_host ne $server)
					or !defined($mountprog) or !$mountprog
					or !defined($file) or !$file or (! -f $file))
				{
					 print STDERR "$0: (bug) bad output from $snfshost: $host_mountprog\n";
					 exit 1;
				}
				
				my $host;
				if ($host = check_hosts_mountprogs($mountprog, \%hosts_mountprogs))
				{ 
					if ($host eq $server)
					{
						die "bug: $server was in %hosts_mountprogs with $mountprog but we didn't find it ?"; 
					}
					else
					{
						print "Found that $server forwards $mountprog, and $host too.\n";
						print $fstab_snfs_fh $original_line;
						next;
					}
				}
				else
				{
					$hosts_mountprogs{$server} = $mountprog ;
				}
			}
			my $delimiter="";
			if ($options ne "")
			{
				$delimiter = ",";
			}
			$options = $options . $delimiter .  "mountprog=$mountprog,nfsprog=" . ($mountprog + SNFS::PROG_OFFSET);
			print $fstab_snfs_fh "${space0}${hostname}:${server_path}${space1}${path}${nfs_field}${space2}${options}${remaining}\n";
		}
		else 
		{
			print $fstab_snfs_fh $line;
		}
	}
	$fstab_fh->close;
		       
}

sub get_hosts_in_ref($$)
{
	my $mountprog = shift;
	my $ref_hosts_mountprogs = shift;
	my @hosts = ();

	foreach my $host (keys(%{$ref_hosts_mountprogs }))
	{
		if ($ref_hosts_mountprogs->{$host} == $mountprog)
		{
			push @hosts, $host;
		}
	}
	return @hosts;
}

sub check_hosts_mountprogs($$ )
{
	my $mountprog = shift;
	my $ref_hosts_mountprogs = shift;
	my $host = "";
	if (defined($ref_hosts_mountprogs))
	{
		my @hosts = ();
		if (@hosts = get_hosts_in_ref( $mountprog,$ref_hosts_mountprogs))
		{
			if ($#hosts + 1 > 1)
			{
				print STDERR "Error, more than one remote host for $mountprog (@hosts)\n";
				exit 1;
			}
			else 
			{
				$host = $hosts[0];
			}
		}
	}
#	print STDERR "host $host for $mountprog\n";
	return $host;
}
