# Defined commands:
#

%cmds = ("quit", "quit",
	 "kill", "kill",
	 "kill_pcn", "kill_pcn",
	 "set", "set",
	 "child-rusage", "child_rusage",
	 "uptime", "uptime",
	 "eval", "eval",
	 "start", "start",
	 "start_fork", "start_fork",
	 "start_fork_withdir", "start_fork_withdir",
	 "startnode", "startnode",
	 "arch", "arch",
	 "status","status",
	 "ps", "ps",
	 "kj", kj);

$arch = &getarch;

%vars = ("pcn-dir", "/Net/auriga/auriga5/olson/PCN/v1.1/Em/$arch.bsd",
	 "pcn", "pcn.$arch.bsd",
	 "enabled", "yes",
	 "see_node_output", "yes",
	 "kill_signal", "15");

%magic_vars = ("see_node_output", "set_see_node_output");

#print "Cmds are ", join(':', %cmds), "\n";

%pcn_pids = ();
%pcn_handles = ();

$next_pcn_handle = "PCN001";

sub startnode
{
    local($old) = select($conn_handle);
    if (&start_node_on_host(@_))
    {
	print $conn_handle "Success\n";
    }
    select($old);
    0;
}

sub child_rusage
{
    local($user, $system, $cuser, $csystem) = times;

    print $conn_handle "$cuser $csystem\n";
    0;
}

sub set
{
    local($var, @value) = @_;

    if (!defined($var))
    {
	while (($var, $value) = each %vars)
	{
	    print $conn_handle "$var = $value\n";
	}
    }
    elsif (!defined(@value))
    {
	print $conn_handle "$var = $vars{$var}\n";
    }
    else
    {
	local($vstr) = join(' ', @value);
	local($old) = $vars{$var};
	print $conn_handle "Assigned $var = $vstr\n";
	$vars{$var} = $vstr;
	if ($magic_vars{$var})
	{
	    print "Evaling magic variable $var sub $magic_vars{$var}\n";
	    eval "&$magic_vars{$var}(\$old, \$value)";
	    undef $@;
	}
    }
    0;
}


# Start a process. This called by start-node from PCN
#
sub start_fork
{
    &start_fork_withdir($vars{"pcn-dir"}, $vars{pcn}, @_);
}

sub start_fork_withdir
{
    local($pcndir, $pcn, @args) = @_;
    local($cmd, $mesg, $rc, $rh, $wh);

    $rh = "${next_pcn_handle}_READ";
    $wh = "${next_pcn_handle}_WRITE";

    pipe($rh, $wh);

    select($rh); $| = 1;
    select($wh); $| = 1;
    select(STDOUT);

    $cmd = "sh -c 'cd $pcndir; $pcn @args'";
    print $conn_handle "Starting process <$cmd>\n";

    if (!$vars{enabled} || $vars{enabled} ne "yes")
    {
	print $conn_handle "Failure: node not enabled\n";
	return 0;
    }

    if (($pid = fork) < 0)
    {
	print $conn_handle "Failure: fork failed: $!\n";
    }
    elsif ($pid == 0)
    {
	# The child
	close(STDIN);
	close(STDOUT);
	close(STDERR);
	close($rh);

	open(STDIN, "</dev/null");
	open(STDOUT, ">&$wh");
	open(STDERR, ">&$wh");
	close($wh);

	chdir($pcndir);
	exec($pcn, @args);
	die "Exec failed: $!\n";
    }

    close($wh);
    $pcn_pids{$rh} = $pid;
    $pcn_handles{$pid} = $rh;

    $next_pcn_handle++;

    print $conn_handle "Success: child $pid started\n";
    print STDOUT "Success: child $pid started\n" if $verbose;

    0;
}

sub set_see_node_output
{
    local($old_value, $new_value) = @_;

    if ($old_value eq "yes" && $new_value ne "yes")
    {
	undef $pcn_save_output;
    }
    elsif ($old_value ne "yes" && $new_value eq "yes")
    {
	&broadcast("start-output:\n",$pcn_save_output);
    }
}

sub process_pcn_output
{
    local($handle) = @_;
    local($rin, $n, $inp);

    &broadcast("start-output:\n");
    while (1)
    {
	$rin = '';
	vec($rin, fileno($handle), 1) = 1;
	$n = select($rin, undef, undef, 0);
	last if $n == 0;

	print "Waiting for pcn input...\n";
	$inp = <$handle>;
	print "Got it\n";
	if (!$inp)
	{
	    local($pid)= $pcn_pids{$handle};
	    local($wpid);
	    print "got EOF\n";
	    $wpid = waitpid($pid, 0);
	    print "Wait returned $wpid\n";
	    &broadcast("Wait returned $wpid\n") if $verbose;
	    &broadcast("got EOF for PCN pid $pid on $hostname\n")
	      if $verbose;
	    &broadcast("PCN node exited on $hostname\n");
	    &broadcast(".\n");
	    close($handle);
	    delete $pcn_pids{$handle};
	    delete $pcn_handles{$pid};
	    return;
	}

	print "got input $inp";
	if ($vars{see_node_output} eq "yes")
	{
	    &broadcast("WORKER: $inp");
	}
	else
	{
	    $pcn_save_output .= $inp;
	}
    }
    &broadcast(".\n") if ($vars{see_node_output} eq "yes");
}    

sub start
{
    local(@args) = @_;
    local($cmd, $mesg, $rc);

    $cmd = "sh -c 'cd ", $vars{"pcn-dir"}, "; ", $vars{"pcn"}, " @args &'";
    $mesg = "Starting process $cmd\n";
    print $mesg;
    print $conn_handle $mesg;
    $rc = system($cmd);
    print "system returns $rc\n";


    if ($rc && 0)		# Hmm. $rc is -1 when it works...
    {
	print $conn_handle "Failure starting process: $!\n";
    }
    else
    {
	print $conn_handle "Success\n";
    }
    0;
}

sub status
{
    local($pid);

    print $conn_handle "PCN node status:\n";
    for $pid (keys %pcn_handles)
    {
	print $conn_handle "PID $pid running with handle $pcn_handles{$pid}\n";
    }
}

sub arch
{
    local($arch) = &getarch;
    print "Got arch $arch\n";
    print $conn_handle "$arch\n";
    0;
}

sub uptime
{
    print $conn_handle `uptime`;
    0;
}

sub eval
{
    local($old) = select($conn_handle);
    print "Eval '@_'\n";
    local(@rc) = eval @_;
    if ($@)
    {
	chop($@);
	print $conn_handle "Eval fails: '$@'\n";
	undef $@;
    }
    else
    {
	print "Eval returns '", join(':', @rc), "'\n";
    }
    select($old);
    0;
}

sub ps
{
    print $conn_handle `ps @_`;
    0;
}
   

sub quit
{
    return 1;
}

sub kj
{
    local($regexp);
    
    print "Got kj, args: ", join(':', @_), "\n";

    $regexp = "(" . join(')|(', @_) . ")";

    print "regexp is '$regexp'\n";

    open(PS_OUT, "/bin/ps ugxaww|");

    while (<PS_OUT>)
    {
	if (/(\S+)\s+(\d+)\s+.*$regexp/i)
	{
	    $user = $1;
	    $pid = $2;

	    if ($user ne $ENV{USER})
	    {
		print $conn_handle "You don't own $pid\n";
		next;
	    }
	    print $conn_handle "Killing process user $user pid $pid\n";
	    if (kill(2, $pid) != 1)
	    {
		kill(9, $pid);
	    }
	}
    }
    close(PS_OUT);
    return 0;
}

sub kill
{
    $done = 1;
    return 1;
}

sub kill_pcn
{
    local(@pids) = @_;
    local($sig, $pid);

    $sig = $vars{kill_signal};

    if (@pids == 0)
    {
	@pids = keys %pcn_handles;
    }

    for $pid (@pids)
    {
	print $conn_handle "Killing PCN pid $pid with signal $sig\n";
	$n = kill $sig, $pid;
	if ($n == 0)
	{
	    print $conn_handle "Pid $pid didn't die\n";
	}
	else
	{
	    print $conn_handle "Kill returns $n\n";
	}
    }
    0;
}

1;


