
sub gethostname
{
  local($hostname);
  chop($hostname = `hostname`);
  $hostname = &full_hostname($hostname);
  $hostname =~ tr/A-Z/a-z/;
  $hostname;
}

sub getarch
{
  return $ENV'arch if defined($ENV'arch);
  local($arch);
  chop($arch = `arch`);
  $arch =~ tr/A-Z/a-z/;
  $arch;
}

if (!do 'fcntl.ph')
{
    sub F_SETFL { 4; }
    sub F_GETFL { 3; }
    sub FNDELAY { 4; }
}

sub noblock
{
    local($handle, $flags);

    for $handle (@_)
    {
	if (($flags = fcntl($handle, &F_GETFL, 0)) < 0)
	{
	    warn "fcntl $handle failed: $!\n";
	    return;
	}
	$flags |= &FNDELAY;
	if (fcntl($handle, &F_SETFL, $flags) < 0)
	{
	    warn "fcntl $handle failed: $!\n";
	    return;
	}
    }
}

sub cmd {
    local(*cmd) = @_;
    %cmd = ('start-node-foo-bar', "snfbasdf");

}

sub compute_abbrevs
{
    local(*cmds, *abbrevs, $level) = @_;
    local($cmd, @a, $a);
    local(%clashes);
    local($gotclash, $regexp);
    local(@nogood);

    $level = 1 unless $level;

    %abbrevs = ();

    for $cmd (keys %cmds)
    {
	$regexp = '[^-]' x $level;
#	print "regexp is '$regexp'\n";
	($a = $cmd) =~ s/($regexp)[^-]*/\1/g;
	$a =~ s/-//g;
#	print "got '$a'\n";
	if (defined($abbrevs{$a}))
	{
	    print "Abbrev clash! $cmd and $abbrevs{$a}\n" if $verbose > 1;
	    $abbrevs{$a} .= ":$cmd";
	    push(@nogood, $a);
	    $gotclash = 1;
	}
	else
	{
	    $abbrevs{$a} = $cmd;
	}
    }


    if ($gotclash)
    {
	local($x,$y);

	while (($x, $y) = each(%abbrevs))
	{
	    if ($y =~ /:/)
	    {
		local(@a) = split(/:/,$y);
		for (@a)
		{
		    $clashes{$_} = $cmds{$_};
		}
	    }
	}
	
#	print "Got clash:\n";
#	while (($x, $y) = each(%clashes))
#	{
#	    print "CL: $x = $y\n";
#	}
	local(%newabbrevs);
	&compute_abbrevs(*clashes, *newabbrevs, $level+1);
#	print "New abbrevs:\n";
	while (($x, $y) = each(%newabbrevs))
	{
	    $abbrevs{$x} = $y;
#	    print "NA: $x = $y\n";
	}
    }
}

sub start_node_on_host
{
    local($name, $user, $cmd) = @_;
    local($rc, $tmpfile);
    local($oldhandler, $oldtime);

    sub alarming { die "TIMEOUT\n"; }

    $oldhandler = $SIG{ALRM};
    $SIG{ALRM} = alarming;

    $oldtime = alarm($vars{"rsh-timeout"});

    eval <<ENDEVAL;

    &open2(READ, WRITE, \"rsh \$name -l \$user /bin/sh\");

#    print WRITE \"echo rshrunning\\n\";
#    print WRITE \"hostname\\n\";
#    print WRITE \"uptime\\n\";
#    print WRITE \"date\\n\";
    print WRITE \"\$cmd \&\\n\";
    print WRITE \"exit\\n\";


    \@out = \<READ\>;

    close(WRITE);
    close(READ);

    # Wait for the rsh process to finish up...
    # Do it inside the eval block in case there's a problem.
    print \"waiting\\n\" if \$verbose;
    \$pid = wait;

ENDEVAL

    alarm(0);

    $SIG{ALRM} = $oldhandler;
    alarm($oldtime);


    print "pid is $pid\n" if $verbose;
    print "Output is:\n<@out>\n" if $verbose;

    chop(@out);

    if ($@)
    {
	print "Startnode failed: $@\n";
	undef $@;
	return undef;
    }

    
    local(@which) = grep(/PCN Node control/, @out);

    if (@which)
    {
	local($version) = $which[0] =~ /version\s*(\S+)/;
	
	print "Node control version $version running on $name\n";
	return 1;
    }
    else
    {
	print "Startnode failed: @out\n";
	print "Startnode output: @out\n" if $verbose;
	return undef;
    }
}

sub get_host_list
{
    local($host, $port);
    local(@list);
    &dir_exists($control_dir, "$control_dir/hosts");

    opendir(DIR, "$control_dir/hosts") || die "Can't opendir $control_dir: $!\n";
    while ($host = readdir(DIR))
    {
	next if $host =~ /^\./;

	$port = &read_host_file("$host");

	next unless $port;

	push(@list, "$host/$port");
    }
    closedir(DIR);
    return @list;
}    

sub get_node_list
{
    local($node, $port);
    local(@list);
    &dir_exists($control_dir, "$control_dir/nodes");

    opendir(DIR, "$control_dir/nodes") || die "Can't opendir $control_dir: $!\n";
    while ($node = readdir(DIR))
    {
	next if $node =~ /^\./;

	$port = &read_node_file("$node");

	next unless $port;

	push(@list, "$node/$port");
    }
    closedir(DIR);
    return @list;
}    

sub dir_exists
{
    local($dir);

    for $dir (@_)
    {
	-d $dir || mkdir($dir, 0755) ||
	    die "Cannot mkdir $dir: $!\n";
    }
}

sub print_list
{
    local($prefix, $linelen, @stuff) = @_;
    local($len);

    $len = length($prefix);
    print $prefix;

    for (@stuff)
    {
	if ($len + length($_) > $linelen)
	{
	    print "\n";
	    print $prefix;
	    $len = length $prefix;
	}
	print $_ . ' ';
	$len += length($_) + 1;
    }
    print "\n";
}

sub append
{
    local(*string, @elts) = @_;

    $string = shift @elts if $string eq "";
    for (@elts)
    {
	$string .= "," . $_;
    }
}

sub hostname_from_addr
{
    local(@addr) = @_;
    local($inetaddr, @info);

    $inetaddr = pack('C4', @addr);
    @info = gethostbyaddr($inetaddr, 2);
    
    $info[0];
}

sub full_hostname
{
    local($host) = @_;

    local($name) = &expand_hostname($host);

    return $name if $name;
    $host;
}

sub expand_hostname
{
    local($host) = @_;

    local($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($host);

    if (!$name)
    {
	if (@addr = $host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
	{
	    ($name, $aliases, $addrtype, $length, @addrs) =
		gethostbyaddr(pack('C4', @addr),2);
	}
    }

    return $name;
}

sub unexpand_filename
{
    local($file) = @_;

    if ($file =~ m,^$ENV{HOME}\b,)
    {
	$file =~ s,,~,;
    }
    $file;
}

sub expand_filename
{
    local($file) = @_;
    local($user, $dir);

    if ($file =~ m,^~([a-zA-Z]+)(/?),)
    {
	$user = $1;
#	print "Expanding user $user\n";
	local(@ary) = getpwnam($user);
	if (@ary)
	{
	    $dir = $ary[7];
#	    print "Got dir $dir\n";
	    $file =~ s,^~[a-zA-Z]+(/?),${dir}$1,;
	}
    }
    elsif ($file =~ m,^~(/?),)
    {
	$file =~ s,^~(/?),$ENV{HOME}$1,;
    }
    return $file;
}

sub flush
{
    local($handle) = @_;
    local($old);

    $old = select($handle); $| = 1;
    select($old);
}


# Select from the connections in @_, returning a list of
# handles.
sub select
{
    local($rin, $handle, $n);
    local(@ready);

    print "Selecting from ", join(' ', @_), "\n" if $watch_select;

    while (1)
    {
	$rin = '';
	for $handle (@_)
	{
	    vec($rin, fileno($handle), 1) = 1;
	}
	
	$n = select($rout = $rin, undef, undef, undef);

#	print "select returns $n\n" if $verbose;
	
	if ($n < 0)
	{
	    next if ($! =~ /interrupted\s*system\s*call/i);
	    die "Select fails: $!\n";
	}
	else
	{
	    last;
	}
    }
	    
    for $handle (@_)
    {
	last if $n == 0;
	if (vec($rout, fileno($handle), 1) == 1)
	{
	    push(@ready, $handle);
	    $n--;
	}
    }
    return @ready;
}
	

sub read_host_file
{
    local($host) = @_;
    local($port, $file);

    -d "$control_dir/hosts" || mkdir("$control_dir/hosts", 0755);

    $file = "$control_dir/hosts/$host";

    open(HOST_FILE, "<$file") || return undef;
    chop($port = <HOST_FILE>);
    close(HOST_FILE);
    return undef if ($port !~ /^\d+$/);
    return $port;
}

sub write_host_file
{
    local($host, $port) = @_;

    -d "$control_dir/hosts" || mkdir("$control_dir/hosts", 0755);

    local($file) = "$control_dir/hosts/$host";

    open(HOST_FILE, ">$file") || die "Can't write $file: $!\n";
    print HOST_FILE "$port\n";
    close(HOST_FILE);
}

sub read_node_file
{
    local($node) = @_;
    local($port, $file);

    -d "$control_dir/nodes" || mkdir("$control_dir/nodes", 0755);

    $file = "$control_dir/nodes/$node";

    open(NODE_FILE, "<$file") || return undef;
    chop($port = <NODE_FILE>);
    close(NODE_FILE);
    return undef if ($port !~ /^\d+$/);
    return $port;
}

sub write_node_file
{
    local($node, $port) = @_;

    -d "$control_dir/nodes" || mkdir("$control_dir/nodes", 0755);

    local($file) = "$control_dir/nodes/$node";

    open(NODE_FILE, ">$file") || die "Can't write $file: $!\n";
    print NODE_FILE "$port\n";
    close(NODE_FILE);
}

sub unlink_node_file
{
    local($node) = @_;

    local($file) = "$control_dir/nodes/$node";
    unlink($file);
}

sub unlink_host_file
{
    local($host) = @_;

    local($file) = "$control_dir/hosts/$host";
    unlink($file);
}

# &open2: tom christiansen, <tchrist@convex.com>
#
# usage: $pid = open2('rdr', 'wtr', 'some cmd and args');
#
# spawn the given $cmd and connect $rdr for
# reading and $wtr for writing.  return pid
# of child, or 0 on failure.  
# 
# WARNING: this is dangerous, as you may block forever
# unless you are very careful.  
# 
# $wtr is left unbuffered.
# 
# abort program if
#	rdr or wtr are null
# 	pipe or fork or exec fails

#package open2;
$fh = 'FHOPEN000';  # package static in case called more than once

#sub main'open2 {
sub open2 {    
	local($kidpid);
	local($dad_rdr, $dad_wtr, $cmd) = @_;

	$dad_rdr ne '' 		|| die "open2: rdr should not be null";
	$dad_wtr ne '' 		|| die "open2: wtr should not be null";

	# force unqualified filehandles into callers' package
	local($package) = caller;
	$dad_rdr =~ s/^[^']+$/$package'$&/;
	$dad_wtr =~ s/^[^']+$/$package'$&/;

	local($kid_rdr) = ++$fh;
	local($kid_wtr) = ++$fh;

	pipe($dad_rdr, $kid_wtr) 	|| die "open2: pipe 1 failed: $!";
	pipe($kid_rdr, $dad_wtr) 	|| die "open2: pipe 2 failed: $!";

	if (($kidpid = fork) < 0) {
	    die "open2: fork failed: $!";
	} elsif ($kidpid == 0) {
	    close $dad_rdr; close $dad_wtr;
	    open(STDIN,  ">&$kid_rdr");
	    open(STDOUT, ">&$kid_wtr");
	    print STDERR "execing $cmd\n" if $verbose;
	    open(STDERR, ">&$kid_wtr");
	    exec $cmd;
	    die "open2: exec of $cmd failed";   
	}
	close $kid_rdr; close $kid_wtr;
	select((select($dad_wtr), $| = 1)[0]); # unbuffer pipe
	$kidpid;
}

sub ltime
{
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
	localtime($_[0]);
    return sprintf("%02d:%02d:%02d %02d/%02d/%02d", 
		   $hour, $min, $sec,
		   $mon+1, $mday, $year);
}

1;
