Path: usenet.cis.ufl.edu!usenet.eel.ufl.edu!spool.mu.edu!howland.reston.ans.net!news.sprintlink.net!psgrain!nntp.teleport.com!usenet
From: [email protected] (Eric Arnold)
Newsgroups: comp.lang.perl.announce,comp.lang.perl.misc,comp.lang.perl
Subject: ANNOUNCE:  "comm_utils.pl" -- perl4/5 easy client/server IPC
Date: 11 Jun 1995 17:51:34 GMT
Organization: Sun Microsystems
Lines: 727
Approved: [email protected] (comp.lang.perl.announce)
Message-ID: <[email protected]>
NNTP-Posting-Host: linda.teleport.com
Xref: usenet.cis.ufl.edu comp.lang.perl.announce:29 comp.lang.perl.misc:562 comp.lang.perl:51868


This is the perl4/5 client/server package that I try to keep updated
for myself.  I just included it in the posting of my "shelltalk"
program, but it's useful in its own right.

-Eric


#                         "comm_utils.pl"
#
# This is a library of IPC goodies.  There is no warrenty, but I'd
# be happy to get ideas for improvements.
#
#               - [email protected]
#
# It's been tested with Perl4/Perl5 and SunOS4.x and Solaris2.3 - 2.5.
# It's normally put into a file and "require"'d, but can also be simply
# concatinated to the end of some other perl script.
#
# A lot was borrowed from "chat2.pl", and then diverged as its goals became
# generalized client/server IPC, support for SVR4/Solaris, and to facilitate
# my "shelltalk" program.
#
# Function summary:
#
#   &init();
#
#   $handle = &comm'open_port($host, $port, $timeout);
#   $handle = &comm'open_listen( $port );
#   ( $Proc_handle, $Proc_tty_handle, $Proc_pid ) = &comm'open_proc($Shell_cmd);
#
#   ( $new_handle, $rem_host ) = &comm'accept_it( $handle );
#   @ready_handles = &comm'select_it( $timeout, $handle1, $handle2, ..... );
#
#   $string = &comm'gets( $handle );
#   &comm'sysread( $handle, $buf, $num_bytes );
#   &comm'print( $handle, $buf );
#   &comm'system( $Proc_tty_handle, $command );
#
#   &comm'close_noshutdown( $handle );
#   &comm'close( $handle )
#
#   &comm'ioctl( $Proc_tty_handle, $ioctl_command, $var );
#   &comm'stty_sane( $handle );
#   &comm'stty_raw( $handle );
#   &comm'get_ioctl_from_stty( $stty_cmd );
#   &comm'dump_ioctl( $stty_cmd );
#
#
# See the end for example programs demonstrating usage.
#
# Bugs:
#   - SOCK_STREAM (see comm_utils.pl) is different for SVR4/Solaris,
#     but if you use a perl compiled under SunOS, then the old value is
#     needed.  How do I tell whether I'm running a SunOS4.x perl or Solaris2?
#     I finally gave up and it first tries 1 for SOCK_STREAM, and if that
#     fails, then it tries 2.
#
# 09/11/94 07:03:04 PM;  eric:  fixed for Solaris and /dev/tty
# 09/14/94 02:11:19 AM;  eric:  close correct file handle in open_listen
# 09/15/94 03:33:31 AM;  eric:  added sock'system
# 09/19/94 10:48:11 AM;  eric:  added cheapo/easy ioctl dump/do
# 10/11/94 11:07:14 AM;  eric:  added I_POP to clear stream on pty
# 11/08/94 03:03:19 PM;  eric:  changed to first try SOCK_STREAM=1, then =2
# 02/28/95 12:53:22 PM;  eric:  found the right place to set SO_LINGER!
# 03/18/95 08:19:46 PM;  eric:  added timeout arg to open_port
# 05/07/95 10:56:25 PM;  eric:  fixed shutdown/close order bug in close()
#                               added close_noshutdown
# 06/08/95 01:06:03 PM;  eric:  fixed Sol2.4 problem with string literal
#                               as last arg to syscall($SYS_ioctl

package comm;

&init;

sub init{
 *Debug = *main'Debug;

 if ( -f "/vmunix" )
 {
   $OS_type = "BSD";
   $SOCK_STREAM=1;
 }
 else
 {
   $OS_type = "SVR4";
   if ( $] >= 5 ){
     $SOCK_STREAM=1;
   }else{
     $SOCK_STREAM=2;}
 }

 print STDERR "OS_type=$OS_type\n" if $Debug;

 chop( $thishost = `uname -n ` );

 $next_handle="stuff000000";
 $sockaddr = 'S n a4 x8';

 $thisaddr = (gethostbyname($thishost))[4];
 $thisproc = pack($sockaddr, 2, 0, $thisaddr);

 $SYS_ioctl = 54;

 if ( $OS_type eq "SVR4" ){
   # These must be syscalls because Perl's ioctl doesn't know about I_PUSH
   #syscall($SYS_ioctl, fileno($_TTY), $I_PUSH, "ptem" );
   #syscall($SYS_ioctl, fileno($_TTY), $I_PUSH, "ldterm");

   # from /usr/include/sys/termios.h
   $tIOC       =( unpack("C", 't') << 8);
   $TIOCGETP       =($tIOC|8);
   $TIOCSETP       =($tIOC|9);

   $TIOC       =( unpack("C", 'T' ) <<8);
   $TCGETS         =($TIOC|13);
   $TCSETS         =($TIOC|14);
   $TCSANOW        =(( unpack("C",'T')<<8)|14); #/* same as TCSETS */
   $TCGETA     =($TIOC|1);
   $TCSETA     =($TIOC|2);

   # From /usr/include/sys/stropts.h
   $STR =             ( unpack("C", "S") <<8 );
   $I_PUSH =          ($STR|02);       #$I_PUSH = 21250;
   $I_POP =           ($STR|03);
   $I_LOOK =          ($STR|04);
   #define I_FLUSH         (STR|05)

 }else{
   $TIOCGETP=0x40067408;       #d(1074164744)
   $TIOCSETP=0x80067409;       #d(-2147060727)
 }

 $SOL_SOCKET      =0xffff          ;#/* options for socket level */
 $SO_DEBUG        =0x0001          ;#* turn on debugging info recording */
 $SO_ACCEPTCONN   =0x0002          ;#* socket has had listen() */
 $SO_REUSEADDR    =0x0004          ;#* allow local address reuse */
 $SO_KEEPALIVE    =0x0008          ;#* keep connections alive */
 $SO_DONTROUTE    =0x0010          ;#* just use interface addresses */
 $SO_BROADCAST    =0x0020          ;#* permit sending of broadcast msgs */
 $SO_USELOOPBACK  =0x0040          ;#* bypass hardware when possible */
 $SO_LINGER       =0x0080          ;#* linger on close if data present */
 $SO_OOBINLINE    =0x0100          ;#* leave received OOB data in line */


}


sub open_port{

 local( $server, $port, $timeout ) = @_;
 local( $new_handle ) = ++$next_handle;
 local( %saveSIG, $ret );

 if ($server =~ /^(\d+)+\.(\d+)\.(\d+)\.(\d+)$/) {
         $serveraddr = pack('C4', $1, $2, $3, $4);
 } else {
         local(@x) = gethostbyname($server);
         return undef unless @x;
         $serveraddr = $x[4];
         printf( "server=$server,serveraddr=%x\n", $serveraddr) if $Debug;
 }

 $serverproc = pack($sockaddr, 2, $port, $serveraddr);
 print STDERR "\$serverproc = pack($sockaddr, 2, $port, ",
   join(".", unpack("C*", $serveraddr)), ");\n" if $Debug;

 unless (socket( $new_handle, 2, 1, 6)) {
   unless (socket( $new_handle, 2, 2, 6)) {
         # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp'
         # but who the heck would change these anyway? (:-)
         ($!) = ($!, close( $new_handle)); # close new_handle while saving $!
         #die "socket failed";
         print STDERR "Socket error $!\n" if $Debug;
         return undef;
   }
 }
 unless (bind( $new_handle, $thisproc)) {
         ($!) = ($!, close( $new_handle)); # close new_handle while saving $!
         #die "bind failed";
         print STDERR "bind error $!\n" if $Debug;
         return undef;
 }

 %saveSIG=%SIG;
 if ( $timeout )
 {
   $SIG{ALRM} = "timedout";
   alarm($timeout);
 }
 eval { $ret = connect( $new_handle, $serverproc) };

 if ( !$ret || ($@ =~ /^timedout/) ) {
         ($!) = ($!, close( $new_handle)); # close new_handle while saving $!
         #die "connect failed, $!";
         print STDERR "connect error eval=($@)$!\n" if $Debug;
         if ( $@ =~ /^timedout/ ) {
           $! .= ", timeout after $timeout seconds";}
         return undef;
 }

 if ( $timeout )
 {
   %SIG = %saveSIG;
   alarm(0);
 }

 select((select( $new_handle), $| = 1)[0]);

 return $new_handle;
}


sub timedout {
 die "timedout";
}



sub open_listen{
 local( $port ) = @_;

 ( $thisproc_local ) = pack( $sockaddr, 2, $port, $thisaddr );
 #print "( $thisproc_local ) = pack( $sockaddr, 2, $port, $thisaddr );\n";

 $new_handle = ++$next_handle;

 unless (socket( $new_handle, 2, 1, 6)) {
   unless (socket( $new_handle, 2, 2, 6)) {
         # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp'
         # but who the heck would change these anyway? ( :- )
         #die "socket failed: $!";
         print STDERR "socket failed: $!\n" if $Debug;
         ($!) = ($!, close($new_handle)); # close S while saving $!
         return undef;
   }
 }


 # We want it it release the socket for immediate reuse if the server is
 # shutdown/restarted.  It seems that SO_LINGER and SO_REUSEADDR are most
 # pertinant, but SO_KEEPALIVE seems like it might be nice too, for
 # notification of peer disappearance.
 $linger = pack("II", 0, 0 );  # linger is a C struct in socket.h
 setsockopt( $new_handle, $SOL_SOCKET, $SO_LINGER, $linger);
 setsockopt( $new_handle, $SOL_SOCKET, $SO_KEEPALIVE, 1);
 setsockopt( $new_handle, $SOL_SOCKET, $SO_REUSEADDR, 1);

 unless ( bind( $new_handle, $thisproc_local )) {
         #die "bind failed: $!";
         print STDERR "bind failed: $!\n" if $Debug;
         ($!) = ($!, close($new_handle)); # close S while saving $!
         return undef;
 }
 unless ( listen( $new_handle, 1 )) {
         #die "listen failed: $!";
         print STDERR  "listen failed: $!\n" if $Debug;
         ($!) = ($!, close($new_handle)); # close S while saving $!
         return undef;
 }

 select( (select( $new_handle ), $| = 1 )[0] );
 local( $family, $port, @myaddr ) = unpack( "S n C C C C x8",
               getsockname( $new_handle ));

 #print " local( family, port, myaddr ) = local( $family, $port, @myaddr) \n";
 return  $new_handle;
}



sub accept_it{ local( $handle ) = @_; local( $addr, $af, $port,
 $inetaddr, $acceptaddr ) = ();

 $new_handle = ++$next_handle;

 unless( ( $addr = accept( $new_handle, $handle ) ) ) {
         print STDERR "accept failed: $!";
 }

 ( $af, $port, $inetaddr ) = unpack( $sockaddr, $addr );
 @inetaddr = unpack( 'C4', $inetaddr );
 #print "accept: $af $port @inetaddr(", join('.', @inetaddr), ")\n";

 #print "host=", gethostbyaddr( join('.', @inetaddr), 2 ), "\n";
 #print "host=", gethostbyaddr( join(' ', @inetaddr), 2 ), "\n";
 ($name, $aliases, $type, $len, $acceptaddr) =
       gethostbyaddr( pack( 'C4', @inetaddr ), 2 );
 #print "accept: host=$name, port=$port\n";

 select( ( select( $new_handle ), $| = 1 )[0] );

 $name = join(".", @inetaddr ) unless $name;
 return $new_handle,$name;
}




sub select_it {
 local( $timeout, @handles ) = @_;

 local( @ready ) = ( );
 local( $rout, $rmask, $handle, $eout, $emask );

 for $handle ( @handles ) {
   vec( $rmask, fileno( $handle ), 1 ) = 1;
   vec( $emask, fileno( $handle ), 1 ) = 1;
 }
 ( $nfound, $timeleft ) = select( $rout=$rmask, undef, $eout=$emask, $timeout );

 print "nfound=$nfound\n" if $DEBUG;
 if ( $nfound < 1 ){
   if ( $nfound < 0 ){
     print "error=$!\n" if $DEBUG; }
   return @ready;
 }

 # You could also do:
 #   @bit = split(//,unpack('b*',$rout));
 #   if ($bit[fileno(STDIN)] == 1){ ... };

 for $handle ( @handles ) {
   if ( vec( $rout, fileno( $handle ), 1 ) == 1 ) {
     print "fh=$handle is ready\n" if $DEBUG;
     push( @ready, $handle ); }
   if ( vec( $eout, fileno( $handle ), 1 ) == 1 ) {
     print "Exception on read_handle=$handle\n" if $DEBUG; }
 }

 return @ready;
}

1;

## $handle = &chat'open_proc("command","arg1","arg2",...);
## opens a /bin/sh on a pseudo-tty

sub open_proc { ## public
       local(@cmd) = @_;

       #local(*TTY,*PTY);      # PTY must not die when sub returns
       local( $pty_handle, $tty_handle );

       $pty_handle = "proc" . ++$next_handle;
       *PTY = $pty_handle;
       $tty_handle = "proc" . ++$next_handle;
       *TTY = $tty_handle;

       local($pty,$tty) = &_getpty(PTY,TTY);

       die "Cannot find a new pty" unless defined $pty;
       local($pid) = fork;
       die "Cannot fork: $!" unless defined $pid;
       unless ($pid) {
               close STDIN; close STDOUT; close STDERR;
               if ( $OS_type eq "SVR4" ){
                 syscall(39,3); #* setsid():: syscall(39,3)
               }else{
                 #syscall(175);#setsid , doesn't seem to work well
                 setpgrp(0,$$);
                 # this ioctl is necessary for "isig" to work right,
                 # and otherwise "csh" freaks out and hangs:
                 if (open(DEVTTY, "/dev/tty")) {
                     ioctl(DEVTTY,0x20007471,0);       # XXX s/b &TIOCNOTTY
                     close DEVTTY;
                 }
               }
               open(STDIN,"<$tty");
               #open(STDIN,"<&TTY");   # fails to assign controlling tty!
               if ( $OS_type eq "BSD" ){
                 # doesn't seem to be necessary if open by filename
                 #TIOCSCTTY,d(536900740)0x(20007484)
                 #syscall(54, fileno(STDIN), 0x20007484,  1 );
               }

               open(STDOUT,">$tty");
               open(STDERR,">&STDOUT");
               die "Oops" unless fileno(STDERR) == 2;  # sanity
               close(PTY) || print "error closing master handle:$!\n";

               exec @cmd;
               die "Cannot exec @cmd: $!";
       }
       #close(TTY);
       return ($pty_handle,$tty_handle,$pid); # return symbol for switcharound

}



sub _getpty { ## private
       local($_PTY,$_TTY)=@_;
       local($pty,$tty);
       $_PTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e;
       $_TTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e;
       for $bank (112..127) {
               next unless -e sprintf("/dev/pty%c0", $bank);
               for $unit (48..57) {
                       $pty = sprintf("/dev/pty%c%c", $bank, $unit);
                       open($_PTY,"+>$pty") || next;
                       select((select($_PTY), $| = 1)[0]);
                       ($tty = $pty) =~ s/pty/tty/;
                       # some stupid magic says I can't use a variable
                       # name in the open for a TTY open
                       open($_TTY,"+>$tty") || next;
                       select((select($_TTY), $| = 1)[0]);
                       if ( $OS_type eq "SVR4" )
                       {
                         local( $pop ) = pack( "p", $pop );
                         syscall($SYS_ioctl, fileno($_TTY), $I_POP, 0 );
                         syscall($SYS_ioctl, fileno($_TTY), $I_POP, 0 );
                         #syscall($SYS_ioctl, fileno($_TTY), $I_LOOK, $pop );
                         #print "looked: len=", length($pop),"($pop)\n";
                         #$pop = pack( "p", $pop );
                         #syscall($SYS_ioctl, fileno($_TTY), $I_LOOK, $pop );
                         #print "looked: len=", length($pop),"($pop)\n";

                         local($tmp);
                         # $tmp needed because Solaris2.4,2.5 complains:
                         # Modification of a read-only value attempted at
                         # comm_utils.pl line  ...
                         # if you use a string literal instead
                         syscall($SYS_ioctl, fileno($_TTY), $I_PUSH, $tmp="ptem" );
                         syscall($SYS_ioctl, fileno($_TTY), $I_PUSH, $tmp="ldterm");
                       }
                       system "stty nl>$tty <$tty";
                       return ($pty,$tty);
               }
       }
       undef;
}


sub gets{
 local(*FH)=shift;
 scalar(<FH>);
}

sub sysread{
 local(*FH)=shift;
 sysread(FH, $_[0], $_[1]);
}

sub ioctl{
 local($fh)=shift;
 syscall( 54, fileno($fh), @_ ) == 0;
}

sub print{
 local($fh)=shift;
 local($ret)= print $fh @_;
 unless ( $ret ){
   print STDERR "Error printing to fh($fh),$!\n"; }
 return $ret;
}


# This is useful when there a parent and child share the file descriptor,
# because the shutdown affects both.
sub close_noshutdown{
 for (@_){
   next unless $_;
   close( $_ );
 }
}

sub close{
 for (@_){
   next unless $_;
   unless ( /^proc/ ){
     print STDERR "Doing shutdown on $_\n" if $DEBUG;
     shutdown($_,2) ;  # must happen before close
   }
   #print "comm'close: $_\n";
   #local( *fh ) = $_; # some god-aweful magic
   #close( $fh );      # some god-aweful magic
   close( $_ );
 }
}


# This is so you can run commands in the pty process without routing
# through the shell, which is ugly

sub system{
 local( $handle, @args ) = @_;
 unless ( fork() ){
   close(STDIN);close(STDOUT);
   open(STDIN,"<&$handle" );
   open(STDOUT,">&$handle" );
   exec ( @args );
   exit;
 }
}



sub stty_sane{
 local( $handle ) = @_;
 local( $packed ) = ();
 #&dump_ioctl( "stty sane erase '^H' " );
 if ( $OS_type eq "SVR4" ){
   $packed = pack("C*", 37,38,0,5,5,173,138,59,0,3,28,8,21,4 );
   ioctl( $handle, $TCSETA, $packed);
 }else{
   $packed = pack("C*", 15,15,8,21,0,216 );
   #$packed = &get_ioctl_from_stty( "stty sane erase '^H' " );
   ioctl( $handle, $TIOCSETP, $packed);
 }
}

sub stty_raw{
 local( $handle ) = @_;
 local( $packed ) = ();
 #&dump_ioctl( "stty raw -echo -icanon eol '^a'" );
 if ( $OS_type eq "SVR4" ){
   $packed = pack("C*", 0,0,0,4,4,189,138,48,0,3,28,8,21,1,1,0,0,22 );
   ioctl( $handle, $TCSETA, $packed);
 }else{
   $packed = pack("C*", 15,15,8,21,0,240 );
   #$packed = &get_ioctl_from_stty( "stty raw -echo -icanon eol '^a'" );
   ioctl( $handle, $TIOCSETP, $packed);
 }
}


sub get_ioctl_from_stty{
 local( $stty_cmd ) = @_;
 local( $ioctl_struct, $get_cmd, $set_cmd, $out, $ret ) = ();

 #print $stty_cmd,"\n";
 system $stty_cmd;

 # These only return 4 bytes.  Why?
 # $p = pack("p", $ioctl_struct );
 #$ret = syscall($SYS_ioctl, fileno(STDIN), $TIOCGETP, $p);
 #$ret = syscall($SYS_ioctl, fileno(STDIN), $TCGETA, $p);

 if ( $OS_type eq "SVR4" ){
   $get_cmd = $TCGETA;
 }else{
   $get_cmd = $TIOCGETP;
 }

 $!=0;
 $ret = ioctl(STDIN, $get_cmd, $ioctl_struct );

 return $ioctl_struct;
 #return ( $ioctl_struct, $ret );      # blows up $ioctl_struct on the stack
}


sub dump_ioctl{
 local( $stty_cmd ) = @_;
 local( $ioctl_struct, $c, $out ) = ();

 $ioctl_struct = &get_ioctl_from_stty( $stty_cmd );
 for $c ( unpack("C*", $ioctl_struct) ){
   #$out .= sprintf("0x%2.2x,", $c );
   $out .= sprintf("%d,", $c );
 }
 print "$stty_cmd = $out \n";
 # I don't know off hand how much of the returned buffer is actually
 # significant; certainly less than the full 256 bytes.
 return $ioctl_struct;
}


1;

__END__

#--------------------------------Example server---------------------------------
#
# Allows multiple client connections, and rebroadcasts data between them.

eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
& eval 'exec perl -S $0 $argv:q'
if 0;

require "comm_utils.pl" unless defined &comm'init;

$Listen_port = 5050;
$Listen_port = $ARGV[0] if $ARGV[0];

$SIG{'HUP'} = "my_exit";
$SIG{'INT'} = "my_exit";
$SIG{'QUIT'} = "my_exit";

$DEBUG = 1;
$|=1;

&comm'init;

if(1)
{
 $Listen_handle = &comm'open_listen( $Listen_port );
 die "open_listen failed on port $Listen_port" unless $Listen_handle;
}
else
{
 # This is optional; it can be useful to use a range of ports
 # if your sockets don't always release a port right away when you kill
 # a process.  However, the "setsockopt()" calls should release the ports
 # for you, so this should no longer be necessary.
 $start_port = $Listen_port;
 {
   if ( ! ( $Listen_handle = &comm'open_listen( $Listen_port ) ) )
   {
     redo unless ( ++$Listen_port <= $start_port + 10 );
     die "open_listen failed on port $Listen_port";
   }
 }
}

print "Listening on port $Listen_port\n" if $DEBUG;

while (1)
{
 @ready_handles = &comm'select_it(1, keys(%Client_handles), $Listen_handle );
 print "Handles ready: @ready_handles\n" if $DEBUG && @ready_handles;

 foreach $handle (@ready_handles)
 {
   if ($handle eq $Listen_handle)
   {
     ($new_handle, $rem_host) = &comm'accept_it($handle);
     $Client_handles{$new_handle} = $rem_host;
     print "New connection from $rem_host\n" if $DEBUG;
   }
   else
   {
     #print &comm'gets($handle);       # if new-line oriented
     if ( &comm'sysread($handle, $buf, 10000) )
     {
       $buf = $Client_handles{$handle} . ": $buf";
       $buf =~ s/[\n]*$/\n/;
       print $buf;
       # rebroadcast data to all clients:
       for $client_handle ( keys %Client_handles ) {
         &comm'print( $client_handle, $buf ); }
     }
     else
     {
       print "Closing handle $handle, host $rem_host\n";
       &comm'close( $handle );
       delete $Client_handles{ $handle };
     }
   }
 }
}


sub my_exit
{
 &comm'close( $Listen_handle );
 print "Closing listen port\n" if $DEBUG;
 exit;
}





#--------------------------------Example client---------------------------------

# Connect to a server, and send STDIN data to it.
# Usage:  tstclient <host> <port>

eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
& eval 'exec perl -S $0 $argv:q'
if 0;

require "comm_utils.pl" unless defined &comm'init;

$Server_port = 5050;
$Server_host = "serverhost.domain";

( $Server_host, $Server_port ) = @ARGV if @ARGV;

$SIG{'HUP'} = "my_exit";
$SIG{'INT'} = "my_exit";
$SIG{'QUIT'} = "my_exit";

$|=1;
$DEBUG = 1;

&comm'init;

if ( ! ( $Server_handle = &comm'open_port($Server_host, $Server_port, 5) ) )
{
 die "open_port failed on host $Server_host, port $Server_port";
}

print "Connected to host $Server_host, port $Server_port\n" if $DEBUG;

while (1)
{
 @ready_handles = &comm'select_it(1, $Server_handle, STDIN);

 foreach $handle (@ready_handles)
 {
   if ($handle eq "STDIN")
   {
     $buf = <STDIN>;
     &comm'print( $Server_handle, $buf );
   }
   else        # server
   {
     unless ( &comm'sysread($handle, $buf, 1000) )
     {
       print "Server connection broken\n";
       &my_exit;
     }
     print $buf;
   }
 }
}


sub my_exit
{
 &comm'close($HANDLE);
 exit;
}