#!/usr/bin/perl
# Script to setup iodtab and pvfsdir files for PVFS
# This file creates both .iodtab and .pvfsdir files and makes sure all
# user entries are correct before creating them.
#
# $Log: mkmgrconf,v $
# Revision 1.5  2004/07/29 12:37:43  rbross
# Applied noninteractive mkmgrconf patch from Garrick Staples <garrick@usc.edu>,
# plus some minor changes.
#
# Revision 1.4  2003/08/22 13:01:19  pcarns
# another mkmgrconf modification to work around a perl/redhat bug, this time
# showing up on redhat 9
#
# Revision 1.3  2003/06/10 23:41:49  pcarns
# Added a work around that permits mkmgrconf to work on Redhat 8
# systems, some of which appear to have a broken perl package as
# best we can tell.
#
# Revision 1.2  2000/12/18 16:52:48  rbross
# Dunno.
#
# Revision 1.1  2000/12/18 16:27:18  rbross
# moved mkiodtab to mkmgrconf
#
# Revision 1.1.1.1  1999/08/10 17:11:31  rbross
# Original 1.3.3 sources
#

use Getopt::Std;

getopts('hr:u:g:m:H:p:P:');

if ($opt_h) {
   print("This script will make the .iodtab and .pvfsdir files\nin the metadata directory of a PVFS file system.\n");
   print "Usage: $0 [options] [hostnames ...]\n";
   print "Options:\n\t-r meta directory root\n\t-u user id\n\t-g group id\n\t-m directory mode\n\t-H mgr hostname\n\t-p mgr port number (use 3000)\n\t-P iod port number (use 7000)\n";
   exit;
}

if (defined($opt_r) ||
    defined($opt_u) ||
    defined($opt_g) ||
    defined($opt_m) ||
    defined($opt_H) ||
    defined($opt_p) ||
    defined($opt_P))
{
   $interactive=0;
} else {
   $interactive=1;
}

# ***********************************
# initialization
# ***********************************

$rootdir = "/";
print("This script will make the .iodtab and .pvfsdir files\nin the metadata directory of a PVFS file system.\n") if $interactive;
if ($< != 0) {
	print("This script should be run as root.\n");
	exit(-1);
}	
print("\n") if $interactive;
#setup STDOUT to flush (unbuffered) -- (confusing, I know!)
select STDOUT; $| = 1;

# ***********************************
# get root directory
# ***********************************
if(defined($opt_r)) {
   $rootdir=$opt_r;
} else {
   print("Enter the root directory (metadata directory):\n");
   $rootdir = <STDIN>;
   chop($rootdir);
}

# ***********************************
# verify directory exists, if not ask to create
# ***********************************
if (!(opendir(DIR, $rootdir))) {
   if ($interactive) {
	print("$rootdir does not exist\n");
	print("Shall we create it?(y or n)\n");
	$resp = <STDIN>;
	chop($resp);
	# if not create, exit program with error
	if ($resp !~ /^y$/) {
		print("Goodbye\n");
		exit(-1);
	}	
   }
	# open new directory with standard permissions (755)
	@temp = split(//,755);
	$temp = $temp[2]*1 + $temp[1]*8 + $temp[0]*64;
	if (!mkdir($rootdir, $temp)) {
		print("$rootdir: mkdir failed (mode = 755)");
		exit(-1);
	}
}	

# ***********************************
# get uid
# ***********************************
# All sections from here on use this while(1) which breaks only when the
# user enters in a correct response (e.g. valid user id).
while (1) {
   if (defined($opt_u)) {
        $user=$opt_u;
   } else {
	print("Enter the user id of directory: \n");
	$user = <STDIN>;
	chop($user);
   }
	# verify uid
	if ($user =~ /^[0-9]+$/) {
		if ((@temp = getpwuid($user)) == NULL) {
			print("$user: Invalid user id...try again\n");
			$interactive ? next : exit(-1);
		}
	}
	else {
		if ((@temp = getpwnam($user)) == NULL) {
			print("$user: Invalid user id...try again\n");
			$interactive ? next : exit(-1);
		}
	}
	last;
}
$uid = $temp[2];

# ***********************************
# get gid
# ***********************************
while (1) {
   if (defined($opt_g)) {
        $group=$opt_g;
   } else {
	print("Enter the group id of directory: \n");
	$group = <STDIN>;
	chop($group);
   }
	# verify gid
	if ($group =~ /^[0-9]+$/) {
		if ((@temp = getgrgid($group)) == NULL) {
			print("$group: Invalid group id...try again\n");
			$interactive ? next : exit(-1);
		}
	}
	else {
		if ((@temp = getgrnam($group)) == NULL) {
			print("$group: Invalid group id...try again\n");
			$interactive ? next : exit(-1);
		}
	}
	last;
}
$gid = $temp[2];

# ***********************************
# get mode
# ***********************************
while(1) {
     if (defined($opt_m)) {
        $mode=$opt_m;
     } else {
	print("Enter the mode of the root directory: \n");
	$mode = <STDIN>;
	chop($mode);
     }
	# verify valid mode
	if ($mode !~ /^[0-7]+$/) {
		print("$mode: Invalid mode...try again\n");
		$interactive ? next : exit(-1);
	}
	$omode = oct($mode);
	if ($omode & ~(oct(777))) {
		print("$mode: Invalid mode...try again\n");
		$interactive ? next : exit(-1);
	}
	last;
}
# make it a directory mode
$omode |= 0040000;

# ***********************************
# get hostname
# ***********************************
while(1) {
    if (defined($opt_H)) {
        $host=$opt_H;
    } else {
	print("Enter the hostname that will run the manager: \n");
	$host = <STDIN>;
	chop($host);
    }
	print("Searching for host...") if $interactive;
	if ((@temp = gethostbyname($host)) == NULL) {
		print("\n$host: Invalid hostname...try again\n");
		$interactive ? next : exit(-1);
	}
	print("success\n") if $interactive;
	last;
}

# ***********************************
# get manager port number
# ***********************************
while(1) {
     if (defined($opt_p)) {
       $port=$opt_p;
     } else {
	print("Enter the port number on the host for manager: \n");
	print("(Port number 3000 is the default)\n");
	$port = <STDIN>;
	chop($port);
     }

	if ($port == NULL) {
		$port = 3000;
		last;
	}
	if ($port < 1024 || $port >= 65536 || $port !~ /^[0-9]+$/) {
		print("$port: Invalid port number...try again\n");
		$interactive ? next : exit(-1);
	}	
	last;
}

# ***********************************
# get I/O node locations
# ***********************************
# LOOP label defined for nested next command below
LOOP: while(1) {
	if (!$interactive) {
		@inodes=@ARGV;
	} else {
		print("Enter the I/O nodes: (can use form node1, node2, ... or\n")
			if $interactive;
		print("nodename{#-#,#,#})\n")
			if $interactive;

	$inputline = <STDIN>;
	chop($inputline);
	# parse input (commas first, then brackets)

#######################################################
# WARNING: BIG HACK!
# the following line crashes on some versions of perl (most often on 
# Redhat 8.0), so we comment it out and replace it with some hopefully
# equivalent regex logic -Phil Carns
#

	# @parse = split(/[, ]+/, $inputline);

	# store input line in default variable 
	$_ = $inputline;
	# initialize array index counter
	$i = 0;
	# while there is still text left
	while($_)
	{
		# strip out the first matching word (delimited by comma or white sp)
		# ANOTHER HACK HERE
		# Apparently redhat 9 doesn't like the following regex, so once
		# again we just try something slightly different but hopefully
		# equivalent -Phil
		#s/^[\s,]*[^\s,]+//;
		s/^[\s,]*[a-z0-9\.]+//i;
		# save the matching string that was stripped out
		$foo = "$&";
		# pull leading and trailing white sp off
		$foo =~ s/[\s,]+//g;
		# stick it in the array
		$parse[$i] = $foo;
		# increment array index
		$i++;
		# loop around and continue on next word
	}

# WARNING: END BIG HACK!
########################################################

	$numinodes = 0;
	for($i=0; $parse[$i] !~ /^$/;$i++) {
		# expand parsed input that contains brackets
		if ($parse[$i] =~ /{/) {
			@arrayline = split(/[{}, ]+/, $parse[$i]);
			$prefix = @arrayline[0];
			$count = @arrayline - 1;
			for ($j = 1; $j <= $count; $j++) { 
				if (@arrayline[$j] =~ /-/) {
					@temparray = split(/-/, $arrayline[$j]);
					for ($k = @temparray[0]; $k <= @temparray[1]; $k++) {
						@inodes[$numinodes] = $arrayline[0].$k;
						$numinodes++;
					}	
				} else { 
					@inodes[$numinodes] = $arrayline[0].$arrayline[$j];
					$numinodes++;
				}	
			}
		}
		else {
			@inodes[$numinodes] = $parse[$i];
			$numinodes++;
		}
	}

    	} #end if interactive
	
	# make sure i/o nodes exist
	# may need to flush here? (what is flush command in perl?)
	print("Searching for hosts...") if $interactive;
	$fail = 0;
	for($i=0;$inodes[$i] !~ /^$/;$i++) {
		if ((@temp = gethostbyname($inodes[$i])) == NULL) {
			@badhosts[$fail] = $inodes[$i];
			$fail++;
		}
	}
	# if any of the lookups failed, try again
	if ($fail) {
		print("\nInvalid I/O node(s): @badhosts...try again\n");
		$interactive ? next LOOP : exit(-1);
	}
	print("success\n") if $interactive;
	last;
}
print("I/O nodes: @inodes\n") if $interactive;

# ***********************************
# get iod port number
# ***********************************
while(1) {
    if ($opt_P) {
        $nodeport=$opt_P;
    } else {
	print("Enter the port number for the iods: \n");
	print("(Port number 7000 is the default)\n");
	$nodeport = <STDIN>;
	chop($nodeport);
    }
	if ($nodeport == NULL) {
		$nodeport = 7000;
		last;
	}
	if ($nodeport < 1024 || $nodeport >= 65536 || $nodeport !~ /^[0-9]+$/) {
		print("$port: Invalid port number...try again\n");
		$interactive ? next : exit(-1);
	}	
	last;
}

# ***********************************
# Write to .iodtab
# ***********************************
$iodtab = ">".$rootdir."/.iodtab";
if (!open(IODTAB, "$iodtab")) {
	print("$iodtab: open error\n");
	exit(-1);
}
print IODTAB ("# .iodtab\n");
$date = localtime(time);
print IODTAB ("# Created by mkiodtab - $date\n");
print IODTAB ("#\n");
print IODTAB ("# node:port #\n");
$count = @inodes;
for ($i = 0; $i < $count; $i++) {
	print IODTAB ("@inodes[$i]:$nodeport\n");
}	
close(IODTAB);
chmod(0755, $rootdir."/.iodtab");

# Write to .pvfsdir
$pvfsdir = ">".$rootdir."/.pvfsdir";
if (!open(PVFSDIR, "$pvfsdir")) {
	print("$pvfsdir: open error\n");
	exit(-1);
}
# get inode from stat structure
$inode = (stat(PVFSDIR))[1];
print PVFSDIR ("$inode\n");
print PVFSDIR ("$uid\n");
print PVFSDIR ("$gid\n");
printf PVFSDIR ("%07o\n", $omode);
print PVFSDIR ("$port\n");
print PVFSDIR ("$host\n");
print PVFSDIR ("$rootdir\n");
print PVFSDIR ("\/\n");
close(PVFSDIR);
chmod(0755, $rootdir."/.pvfsdir");

# finished
print ("Done!\n") if $interactive;
