#!/usr/local/bin/perl
#
# TmmRemoteD
#                               COPYRIGHT (C) 2002, SANO Taku & YMIRLINK Inc.
#                                               email: mikage@ymirlink.co.jp
#
# $Id: tmmremoted.pl,v 1.11 2002/04/07 16:00:33 mikage Exp $
#

use strict;

use Fcntl;
use IPC::Open3;
use IO::Handle;
use IO::Select;

use Digest::MD5;

use vars qw($scriptfile);

$| = 1;

sub errorexit {
  my $mes = shift;
  syswrite(*STDOUT, $mes);
  unlink($scriptfile) if($scriptfile);
  exit;
}


$SIG{PIPE} = sub {
  &errorexit("!!! Broken pipe. !!!\n");
};
$SIG{ALRM} = sub {
  &errorexit("!!! Timeout. !!!\n");
};


my $exittimeout = 600;
alarm($exittimeout);


my $salt = new Digest::MD5;
$salt->add(time);
$salt->add(rand() . rand());
$salt->add($$);
$salt->add(rand() . rand());
$salt->add(`free`);
my $saltmd5 = $salt->hexdigest;

syswrite(*STDOUT, $saltmd5, 32);
alarm($exittimeout);


if(!open(PASS, "$ARGV[0]"))
  {
    &errorexit("!!! can't open $ARGV[0]. !!!\n");
  }
my $pass = <PASS>;
close(PASS);

my $auth;
my $authsize = sysread(*STDIN, $auth, 32);
if(!defined($authsize))
  {
    &errorexit("!!! auth read error (null). !!!\n");
  }
elsif($authsize != 32)
  {
    &errorexit("!!! auth read error (size). !!!\n");
  }
alarm($exittimeout);

my $myauth = Digest::MD5->new->add($saltmd5)->add($pass)->hexdigest;

if($myauth eq $auth)
  {
    my $result = "ok\n";
    syswrite(*STDOUT, $result);
    alarm($exittimeout);
  }
else
  {
    &errorexit("ng\n");
  }


my $cmdline = '';
while(1)
  {
    my $readsize = sysread(*STDIN, $cmdline, 1, length($cmdline));
    if(!defined($readsize))
      {
	&errorexit("!!! cmdline read err (null). !!!\n");
      }
    if($readsize != 1)
      {
	&errorexit("!!! cmdline read err (size). !!!\n");
      }
    alarm($exittimeout);
    if($cmdline =~ s/\n//)
      {
	last;
      }
  }

fcntl(*STDIN, F_SETFL,
      fcntl(*STDIN, F_GETFL, 0) | O_NONBLOCK);

my $script;
if($cmdline =~ s/^#!//)
  {
    while (1)
      {
	my $readsize = sysread(*STDIN, $script, 65536, length($script));
	if (!defined($readsize))
	  {
	    last;
	    &errorexit("!!! script read err (null). !!!\n");
	  }
	elsif ($readsize == 0)
	  {
	    last;
	  }
	else
	  {
	  }
	alarm($exittimeout);
      }

    umask(077);
    my $time = time;
    $scriptfile = "/tmp/tmmremote.$$.$time";
    if(!open(CMDFILE, ">$scriptfile"))
      {
	&errorexit("!!! script write err. [$scriptfile] !!!\n");
      }
    print CMDFILE '#!' . $cmdline . "\n";
    $script =~ tr/\r//d;
    print CMDFILE $script;
    close(CMDFILE);
    chmod(0700, $scriptfile);
    $cmdline = $scriptfile;
  }

my ($cmdwh, $cmdrh, $cmdeh);
$cmdwh = new IO::Handle;
$cmdrh = new IO::Handle;
$cmdeh = new IO::Handle;

my $pid = open3($cmdwh, $cmdrh, $cmdeh,
		$cmdline);


syswrite(*STDOUT, "pid: $pid\n");
alarm($exittimeout);

foreach my $fh ($cmdwh, $cmdrh, $cmdeh, *STDOUT)
  {
#    print "set... $fh\n";
    $fh->autoflush(1);
    fcntl($fh, F_SETFL,
	  fcntl($fh, F_GETFL, 0) | O_NONBLOCK);
  }

my $timeout = 0.1;
my $s = new IO::Select;

# $s->add($cmdwh);
$s->add($cmdrh);
$s->add($cmdeh);
$s->add(*STDIN);
# $s->add(*STDOUT);


my $outputbuff = {};
my $closed = 0;

main_loop:

while(1)
  {
#    print STDERR "check read\n";
    my (@rh) = $s->can_read($timeout);

    foreach my $rh (@rh)
      {
	next if ($rh eq *STDOUT);
	next if ($rh eq $cmdwh);
	my $buff;
	my $rsize = sysread($rh, $buff, 65536);
	if(!defined($rsize))
	  {
	    &errorexit("!!! sysread error. !!!\n");
	  }
	
	alarm($exittimeout);
#	print "read[$rh] $buff\n";

	if($rh eq *STDIN)
	  {
#	    print "add $cmdwh\n";
	    $outputbuff->{$cmdwh} .= $buff;
	    $s->add($cmdwh);
	  }
	else
	  {
#	    print "add STDOUT\n";
	    $outputbuff->{*STDOUT} .= $buff;
	    $s->add(*STDOUT);
	  }
	
	if($rsize == 0)
	  {
	    $s->remove($rh);
#	    print "$rh: closed\n";
	    if(($rh eq $cmdrh) or ($rh eq $cmdeh))
	      {
		$closed++;
		if($closed == 2)
		  {
		    last main_loop;
		  }
	      }
	    if($rh eq *STDIN)
	      {
		kill 1 => $pid;
		unlink($scriptfile) if($scriptfile);
		exit;
	      }
	  }
      }

#    print STDERR "check write\n";
    my (@wh) = $s->can_write($timeout);

    foreach my $wh (@wh)
      {
	next if ($wh eq *STDIN);
	next if ($wh eq $cmdrh);
	next if ($wh eq $cmdeh);
	my $wsize = syswrite($wh, $outputbuff->{$wh});
	if(!defined($wsize))
	  {
	    &errorexit("!!! syswrite error. !!!\n");
	  }
	alarm($exittimeout);
	substr($outputbuff->{$wh}, 0, $wsize) = '';
#	print "$wh: $wsize\n";
	

	if(length($outputbuff->{$wh}) == 0)
	  {
	    $s->remove($wh);
#	    print "$wh: buff empty\n";
	  }
      }
    
  }

# syswrite(*STDOUT, "!!! Waiting... Process Terminate. !!!\n");

my $killid = waitpid($pid, 0);
if($killid)
  {
    syswrite(*STDOUT, "!!! Process Terminated. !!!\n");
  }
else
  {
    syswrite(*STDOUT, "!!! process terminate error. !!!\n");
  }

unlink($scriptfile) if($scriptfile);

