#!/usr/bin/perl -w
# vim:ts=4
# $Id: inetd2rlinetd.in,v 1.9 2006-03-16 19:40:36 robert Exp $

my %services=();
my $dir="/etc/rlinetd.d";
my $ifile="";
my @lines=();

while ( defined($opt = $ARGV[0]) && ($opt eq "-f" || $opt eq "-l" ) ) {
	shift(@ARGV);
	$arg = shift(@ARGV);
	die "Option \`$opt' requires an argument\n" unless ($arg);
	$ifile =  $arg if $opt eq "-f";
	push(@lines, $arg) if $opt eq "-l";
}

$dir = $ARGV[0] if defined $ARGV[0];
die "Directory $dir does not exist\n" unless -d $dir;
die "Options \`-f' and \`-l' cannot be mixed together\n" if ($ifile ne "" && $#lines > -1);

if ($#lines > -1) {
	for $_ (@lines) {
		&add_single_line($_);
	}
	
} else {

	$ifile = "-" if $ifile eq "";
	open IFILE, $ifile or die "Cannot open $ifile: $!\n";
	while (<IFILE>) {
		&add_single_line($_);
	}
	close IFILE;

}	

&output_services();

exit 0;

sub warn {
	$_ = shift;

	print STDERR $_;
	return 1;
}

sub add_single_line {
	$_ = shift;

	chomp;
	s/\\t/ /g;
	return if(/^\s*#/);
	return unless(/^(\S+)\s+\S+\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/);

	my $out		= "";
	my $server	= "";
	my $gid		= "";
	my $rpcvers	= "";
	my $instances	= "";
	my $name	= $1;
	my $proto	= $2;
	my $wait	= $3;
	my $uid		= $4;
	my $exec	= $5;

	&warn("skipping internal service: $name\n") && return 
		if ($exec eq "internal");

	if($proto =~ /^rpc\/(.+)$/) {
		$proto = $1;
		if($name =~ /^(.+)\/(.+)$/) {
			$name = $1;
			$rpcvers = $2;
		}
	}

	&warn("unknown protocol: $proto\n; skipping service $name") && return 
		unless ($proto eq "udp" || $proto eq "tcp");

	if($uid =~ /^(.+)\.(.+)$/) {
		$uid = $1;
		$gid = $2;
	}

	if($exec =~ /^(\S+)\s+(.+)$/) {
		if($1 ne $2) {
			$exec = $2;
			$server = $1;
		}
	}

	if($wait =~ /^nowait\.(\d+)$/) {
		$instances = $1;
	}	

	$out .= $services{$name} if defined $services{$name};

	$out .= "service \"${name}_${proto}\" {\n";
	$out .= "\tprotocol $proto;\n";
	$out .= "\tport \"$name\";\n" unless ($rpcvers);
	$out .= "\tuser \"$uid\";\n";
	$out .= "\texec \"$exec\";\n";
	$out .= "\tgroup \"$gid\";\n" if($gid);
	$out .= "\tserver \"$server\";\n" if($server);
	$out .= "\tinstances $instances;\n" if($instances);
	$out .= "\twait yes;\n" if ($wait eq "wait");
	$out .= "\trpc {\n\t\tname \"$name\";\n\t\tversion $rpcvers;\n\t}\n" if($rpcvers);
	$out .= "}\n";

	$services{$name} = $out;

}

sub output_services {

	my $tempfile = "$dir/.inetd2rlinetd.$$";
	my $realfile; 

	for my $service (keys %services) {
		open OFILE, ">$tempfile" or die "Cannot open temporary file for writing: $!\n";
		print OFILE "# This file was automatically generated by inetd2rlinetd\n\n";
		print OFILE $services{$service};
		close OFILE;
		
		$realfile = "$dir/$service";
		my $mode= (-e $realfile) ? &ask_user($realfile, $tempfile) : "rename";
		

		if ($mode eq "append") {
			open OFILE, ">>$realfile" or die "Cannot open file $realfile: $!\n";
			print OFILE "\n" . $services{$service};
			close OFILE;
		}
		elsif ($mode eq "rename") {
			rename($tempfile, $realfile) or die "Cannot rename tempfile: $!\n";
		}

		unlink $tempfile;
	}


}


sub ask_user {
	my $real_f = shift;
	my $tmp_f  = shift;

	# check if the two files are different
	open FILE, "<$real_f" or die "Cannot open file $real_f: $!";
	my @real_data = <FILE>;
	close FILE;
	open FILE,  "<$tmp_f"	or die "Cannot open file $tmp_f: $!";
	my @tmp_data  = <FILE>; 
	close FILE;
	
	# they don't differ, don't 
	return "skip" if join('', @real_data) eq join('', @tmp_data);


	my $mode="";
	my $input = undef;
	my $on_tty = ( -t STDERR ) and 
		  	( -t STDIN or open $input, "</dev/tty" );
	if (! $on_tty ) {
		&warn(  "File $real_f already exists.\n" .
	        	"I'm refusing to overwrite it with different version,\n" .
			"when I'm not running on the terminal.\n");
		return "skip"
	}

	while ($mode eq "") {

		print STDERR "\n\nFile $real_f already exists\n\n";
		print STDERR "* Show [d]ifference between new version\n";
		print STDERR "* [O]verwrite the file\n";
		print STDERR "* [S]kip the file\n";
		print STDERR "* [A]ppend to the end of the file\n\n";

		do {
			print STDERR "Please choose: [D/O/S/A]: ";
			$_=  (defined $input) ? <$input> : <STDIN>  ;
			chomp;
			$_= uc $_;

			$mode = "rename" 	if ($_ eq "O");
			$mode = "skip"		if ($_ eq "S");
			$mode = "append"	if ($_ eq "A");
			$mode = "diff"		if ($_ eq "D");		
		} until ($mode ne "");

		if ($mode eq "diff") {
			system "diff", "-Nus", "$real_f", "$tmp_f";
			$mode="";
		}

	}

	close $input if defined ($input ) ;
	return $mode;
}


