package hostdir;
use strict;
use auto_install;
use error;
use diskless;
use Socket;
1;

# Get a ordered list of options that the user may set
sub getoptiondescn
{
	my $self = shift;
	my $n = shift;
	return $self->getoptiondesc("host") if ($n == 0);
	return $self->getoptiondesc("mailname") if ($n == 1);
	return undef;
}

# Get description for user settable option $id
sub getoptiondesc
{
	my $self = shift;
	my $id = shift;
	my $temp = undef;

	if ($id eq "host" or $id eq "mailname")
	{
		$temp = gethostbyaddr(inet_aton($self->{"ip"}), AF_INET) || ".invalid.";
		$temp =~ s/^([^\.]+)\..*/$1/;
	}
		
	return
	{
		id => "host",
		prompt => "Name of client",
		default => $temp,
		valid => '^[^\.]*$',
		help => 
"This option specifies the name of the diskless NFS client computer.
",
	} if ($id eq "host");

	return
	{
		id => "mailname",
		prompt => "Default mail host name",
		default => $temp,
		valid => '^[^\.]*$',
		help => 
"This option specifies the default name to be used for out going mail.
The mail domain specified in the group setup will be appended to the
end of this value to form the FQDN.
",
	} if ($id eq "mailname");

	return(undef);
}

# Get value of user settable option, will return "." if value is
# to be taken from the imagedir.
sub getoption
{
	my $self=shift;
	my $option=shift;

	if (defined($self->getoptiondesc($option)))
	{
		return($self->{$option});
	}
	else
	{
		return undef;
	}
}

# Get the REAL value, no matter where it may be stored. This value
# will never be "."
sub getoptionvalue
{
	my $self=shift;
	my $option=shift;

	my $value = $self->getoption($option);
	return($value);
}

# Set the value of user settable option, can be "." if value is
# to be taken from the imagedir.
sub setoption
{
	my $self=shift;
	my $option=shift;
	my $value=shift;

	if (defined($self->getoptiondesc($option)))
	{
		$self->{$option}=$value;
		return 1;
	}
	else
	{
		return 0;
	}
}

#------------------------------------------
sub get_filename_config
{
        my $self=shift;
        return $self->{"hostdir"}."/etc/diskless-host/config";
}

sub get_filename_configm4
{
        my $self=shift;
        return $self->{"hostdir"}."/etc/diskless-host/config.m4";
}

# create a new object
sub new {
        my $proto = shift;
        my $class = ref($proto) || $proto;
        my $self = {
                ip => undef,
		hostdir => undef,
                host => undef,
		theimagedir => undef,
        };
        bless($self,$class);
        return $self;
}

# Get object, if it exists
sub get
{
	my $class=$_[0];
	my $theimagedir=$_[1];
	my $ip=$_[2];
	my $error=$_[3];
	die if (!ref($error) or defined ($$error));

	my $host=undef;
	my $hostdir = $theimagedir->get_dirname_host($ip);
	my $rc;

	if ( ! -d $hostdir )
	{
		$$error="error"->new("error"->notfound,
			"Host directory does not exist");
		return undef;
	}

	if ( ! -d "$hostdir/etc/diskless-host" )
	{
		$$error="error"->new("error"->general,
			"Not a host directory: $hostdir/etc/diskless-host doesn't exist or isn't a directory");
		return undef;
	}

	my $self=$class->new;
	$self->{"ip"} = $ip;
	$self->{"hostdir"} = $hostdir;
	$self->{"theimagedir"} = $theimagedir;

	my $config = $self->get_filename_config;
	if (! -f "$config" )
	{
		return $self;
	}

	$rc = open(FILE,"<$config");
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot open $config for reading: $!");
		return undef;
	}

	while (<FILE>)
	{
		if (!(/^([A-Za-z_0-9]+)=(.*)/))
		{
			$$error="error"->new("error"->general,
				"$config contains an invalid entry");
			return undef;
		}
		if ( $self->setoption($1,$2) )
		{
			# All done
		}
		elsif (
			$1 eq "ip"
			)
		{
			if ($self->{$1} ne $2)
			{
				$$error="error"->new("error"->general,
					"$config entry $1 doesn't match reality");
				return undef;
			}
		}
		else
		{
			$$error="error"->new("error"->general,
				"$config contains unknown entry $1");
			return undef;
		}
	}

	$rc=close(FILE);
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot close $config: $!");
		return undef;
	}

	return $self;
}

# Create object if it doesn't exist
sub create
{
	my $class=$_[0];
	my $theimagedir=$_[1];
	my $ip=$_[2];
	my $error=$_[3];
	die if (!ref($error) or defined ($$error));

	my $hostdir = $theimagedir->get_dirname_host($ip);
	my $rc;

	if ( -d $hostdir )
	{
		$$error="error"->new("error"->alreadyexists,
			"Attempt to create group that already exists");
		return undef;
	}

	$rc = "diskless"->mymkdir("$hostdir",$error);
	return undef if (!$rc);

	$rc = "diskless"->mymkdir("$hostdir/etc",$error);
	return undef if (!$rc);

	$rc = "diskless"->mymkdir("$hostdir/etc/diskless-host",$error);
	return undef if (!$rc);

	my $self=$class->new;
	$self->{"ip"} = $ip;
	$self->{"hostdir"} = $hostdir;
	$self->{"theimagedir"} = $theimagedir;
	return $self;
}


# Save values of user-settable options
sub save
{
	my $self=$_[0];
	my $error=$_[1];
	die if (!ref($error) or defined ($$error));

	my $rc;
	my $host=$self->{"host"};
	my $hostdir=$self->{"hostdir"};
	my $ip=$self->{"ip"};

	return 0 if (!defined($self));

	my $config = $self->get_filename_config;
	my $configm4 = $self->get_filename_configm4;
	$rc = open(FILE,">$config");
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot open $config for writing: $!");
		return 0;
	}
	
	$rc = open(FILEM4,">$configm4");
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot open $configm4 for writing: $!");
		return 0;
	}
	print FILEM4 "divert(-1)dnl\n";

	print FILE "ip=$ip\n";
	print FILEM4 "define(<[host_ip]>,<[$ip]>)\n";

	my $n=0;
	my $optiondesc = $self->getoptiondescn($n);
	while (defined($optiondesc))
	{
		my $value = $self->getoption($optiondesc->{"id"});
		if ($value ne "." and defined($value))
		{
			my $id = $optiondesc->{"id"};
			print FILE $id,"=",$value,"\n";
			print FILEM4 "define(<[host_$id]>,<[$value]>)\n";
		}

		$n++;
		$optiondesc = $self->getoptiondescn($n);
	}

	$rc=close(FILE);
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot close $config: $!");
		return 0;
	}

	print FILEM4 "divert(0)dnl\n";
	$rc=close(FILEM4);
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot close $configm4: $!");
		return 0;
	}

	return 1;
}

# Update all other files in object
sub update
{
	my $self=$_[0];
	my $error=$_[1];
	die if (!ref($error) or defined ($$error));

	my $list = [ $self ];

	"hostdir"->update_many($list,$error) or
		return(0);

	return 1;
}

sub convert
{
	my $src_name_full=shift;
	my $dst_name_full=shift;
	my $ASRC=shift;
	my $theimagedir=$ASRC->{"theimagedir"};
	my $ADST=shift;
	my $self=undef;
	$self = $ADST->{"self"} if (defined($ADST));

	my $error = shift;

	die if (!ref($error) or defined ($$error));


	my @file_list;
	push @file_list, "/usr/lib/diskless/diskless.m4";
	push @file_list, $theimagedir->get_filename_configm4;
	push @file_list, $self->get_filename_configm4 if (defined($self));
	push @file_list, $src_name_full;

	open(OLDOUT, ">&STDOUT");
	my $rc=open(STDOUT, ">$dst_name_full");
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot open $dst_name_full for writing: $!");
		return 0;
	}

	my $rc1=system("/usr/lib/diskless/stderr-die","m4","-E",@file_list);
	my $rc2=close(STDOUT);
	open(STDOUT, ">&OLDOUT");
	close(OLDOUT);

	if ($rc1 != 0)
	{
		$$error = "error"->process(
 			"Error converting $src_name_full to $dst_name_full",$rc1);
		return(0);
	}

	if (!$rc2)
	{
		$$error="error"->new("error"->general,
			"Cannot close $dst_name_full: $!");
		return 0;
	}

	return (1);
}

# Update all other files in many objects of this type
sub update_many
{
	my $class=$_[0];
	my $list=$_[1];
	my $error=$_[2];
	die if (!ref($error) or defined ($$error));

	return 1 if ($#$list == -1);

	my $theimagedir=$list->[0]{"theimagedir"};

	my @SRC;
	my @DST;
	my %OPTIONS;
	my $rc;

	$SRC[0]{"dir"}   = $theimagedir->get_dirname_usrtemplate;
	$SRC[0]{"rules_file"} = $theimagedir->get_filename_usrtemplaterules;
	$SRC[1]{"dir"}   = $theimagedir->get_dirname_imagedir;
	$SRC[1]{"rules_file"} = $theimagedir->get_filename_imagedirrules;

	for (my $i=0; $i<=$#$list; $i++)
	{
		if ( $theimagedir ne $list->[$i]{"theimagedir"} )
		{
			$$error="error"->new("error"->general,
				"Imagedir must match for ALL hosts");
			return(0);
		}

		my $hostdir=$list->[$i]{"hostdir"};

		my $j = $#DST + 1;
		$DST[$j]{"self"} = $list->[$i];
		$DST[$j]{"dir"} = $hostdir;
		$DST[$j]{"allcur"} = "$hostdir/etc/diskless-host/config.all";
		$DST[$j]{"allnew"} = "$hostdir/etc/diskless-host/config.new";
		$DST[$j]{"alltmp"} = "$hostdir/etc/diskless-host/config.tmp";
	}

	for (my $i=0;$i<=$#SRC;$i++)
	{
		my $tmpfile = "/tmp/diskless.$$.converted.$i";
		$SRC[$i]{"theimagedir"} = $theimagedir;
		$rc=convert($SRC[$i]{"rules_file"},$tmpfile,
			$SRC[$i],undef,$error);
		return(0) if (!$rc);
		$SRC[$i]{"rules_file"} = $tmpfile;
	}

	$OPTIONS{"convert"} = \&convert;
	$OPTIONS{"getpackage"} = sub {"diskless"->getpackage(@_)};
	$OPTIONS{"debug"} = 0;			# debugging message enabled
	$OPTIONS{"noaction"} = 0;		# changes prohibited

	auto_install->start(\@SRC,\@DST,\%OPTIONS,$error) or
		return 0;
	
	for (my $i=0;$i<=$#SRC;$i++)
	{
		my $dst_name_full = $SRC[$i]{"rules_file"};
		my $rc = unlink($dst_name_full);
		if (!$rc)
		{
			$$error="error"->new("error"->general,
				"Cannot unlink $dst_name_full: $!");
			return undef;
		}
	}

	for (my $i=0; $i<=$#$list; $i++)
	{
		$list->[$i]->update_post($error) or
			return 0;
	}
	
	return 1;
}

# PRIVATE: Finish of job of update_many
sub update_post
{
	my $self=$_[0];
	my $error=$_[1];
	die if (!ref($error) or defined ($$error));

	my $hostdir=$self->{"hostdir"};
	my $theimagedir=$self->{"theimagedir"};
	my $ip = $self->{"ip"};

	return 1 if ( ! -d "/tftpboot" );

	my $rc=1;
	if ( -l "/tftpboot/$ip" )
	{
		$rc=unlink("/tftpboot/$ip");
	}
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot delete symlink /tftpboot/$ip: $!");
		return 0;
	}

	my $imagedir  = $theimagedir->get_dirname_imagedir;
	$rc=symlink("$imagedir","/tftpboot/$ip");
	if (!$rc)
	{
		$$error="error"->new("error"->general,
			"Cannot create symlink at /tftpboot/$ip: $!");
		return 0;
	}

	return(1);
}
