[comp.lang.perl] Question about two-sided connections to perl

arielf@taux01.UUCP (Ariel Faigon) (01/17/90)

--- Question no. 1:
Is there a simple way to connect a perl program to both sides of a process ?
i.e. I would love to be able to do something like:

	open(OUTPTY,"| some_program |",INPTY) ...;

pipe input into 'some_program'
and read its output in order to post-process it.

Of course, suppose 'some_program' is "interactive" in nature and the
'pipes' are actually pseudo-ttys (otherwise, a deadlock between perl
& 'some_program' is likely to occur). So I assume I can write to
'some_program' and read its immediate "reply" alternately.

This brings me to:

--- Question no. 2:
Can I make 'some_program' that is connected to Perl, think it is writing into
a pty instead of a pipe (actually what I need is that 'some_program' _output_
be unbuffered - note, I'm not asking about unbuffering perl-output which
is simple).

I'm afraid I'll need to write all the 'pty' stuff myself which I
can do in 'C' in the first place.

Any help ?
-- 
Ariel Faigon, CTP group, NSTA
National Semiconductor (Israel)
6 Maskit st.  P.O.B. 3007, Herzlia 46104, Israel   Tel. (972)52-522312
arielf%taux01@nsc.com   @{hplabs,pyramid,sun,decwrl} 34 48 E / 32 10 N

hakanson@ogicse.ogc.edu (Marion Hakanson) (01/18/90)

In article <3196@taux01.UUCP> arielf@taux01.UUCP (Ariel Faigon) writes:
>Is there a simple way to connect a perl program to both sides of a process ?

Not without creating your own pipeline.  And even then, under some
OS's (SunOS-4, for example), there are still a few problems with
"forked" filehandles.  But read on.

>--- Question no. 2:
>Can I make 'some_program' that is connected to Perl, think it is writing into
>a pty instead of a pipe (actually what I need is that 'some_program' _output_
>be unbuffered - note, I'm not asking about unbuffering perl-output which
>is simple).
>
>I'm afraid I'll need to write all the 'pty' stuff myself which I
>can do in 'C' in the first place.

I had exactly this in mind when I built the following as a test
program.  It works in my "real" application, as well.  I may have
posted this script before, but it was in a different context.  Note
that the original parent perl is doing the input (writing to the
middle process), the "child 2" is collecting the output from the
middle process, and "child 1" just sets up the filehandles and exec's
your "some_program".

As I hinted above, under SunOS-4, whatever the parent writes in also
comes back to child 2, in addition to going to the middle process, so
child 2 may have to filter some garbage mixed with the output from the
middle process.  This seems to be related to stdio, and not to PTY's
(I've gotten the same results from using plain Perl pipes).

Anyway, first comes my "ptytst5.pl", followed by my "getpty.pl".  No,
you don't need any C code to use pty's.  The only difficulty (other
than that described above) is that sometimes not all stages of the
pipeline will die when one of them does.  Sigh.

-- 
Marion Hakanson         Domain: hakanson@cse.ogi.edu
                        UUCP  : {hp-pcd,tektronix}!ogicse!hakanson

===========cut here============
#!/usr/bin/perl
# Pseudo-tty test program -- 89/12/01
#   Marion Hakanson (hakanson@cse.ogi.edu)
#   Oregon Graduate Institute of Science and Technology

do 'getpty.pl';
die "$@, aborted" if $@;

$MAST = 'MASTER';
$SLAV = 'SLAVE';
($mast,$slav) = do getpty($MAST,$SLAV);
print STDERR "getpty returns '$mast','$slav'\n";
die 'Cannot get pty, aborted' if ($mast eq '');

if ( fork ) {			# parent
  close($SLAV);		# not needed


  if ( fork ) { 	# still parent
    open(MASTOUT, "+>&$MAST") || die "Cannot dup $MAST to MASTOUT, aborted";
    close($MAST);
    select(MASTOUT); $| = 1;
    select(STDOUT); $| = 1;

    for ($i=0; $i<10; $i++ ) {
      print MASTOUT "LINE out $i\n" || die "Cannot print to $mast, aborted";
      sleep(1);
    }
    exit(0);
  } else {		# child 2
    open(MASTIN, "+>&$MAST") || die "Cannot dup $MAST to MASTIN, aborted";
    close($MAST);

    while ($mastin = <MASTIN>) {
      print STDOUT "$$: $mastin";
    }
    exit(0);
  }
} else {				# child 1
  close($MAST);	# not needed

  open(STDOUT, "+>&$SLAV") || die "Cannot dup $SLAV to STDOUT, aborted";
  open(STDIN, "+>&$SLAV") || die "Cannot dup $SLAV to STDIN, aborted";
  close($SLAV);

  select(STDOUT); $| = 1;

  exec ('tr','A-Z','a-z') || die "Cannot start 'tr', aborted";
}
===========cut here============
#
# $Id: getpty.pl,v 1.3 90/01/02 17:18:56 hakanson Exp $
#
# Perl subroutine to allocate a free pseudo-tty (master/slave pair).
#   Marion Hakanson (hakanson@cse.ogi.edu)
#   Oregon Graduate Institute of Science and Technology

sub getpty {

  local ($MASTER,$SLAVE) = @_;
  local ($master,$slave);
  
  $master = '';
  $slave = '';

  pty: while ( </dev/pty*> ) {
#    print STDERR "trying '$_'\n";
    $master = $_;
    unless ( open($MASTER,"+>$master") ) {
#      print STDERR "open failed: $master\n";
      $master = '';
      next pty;
    }
    
    s/pty/tty/;
    $slave = $_;
    last pty if ( open($SLAVE,"+>$slave") );

#    print STDERR "open failed: $slave\n";
    close($MASTER);
    $master = '';
    $slave = '';
  }

#  print STDERR "getpty returning '$master','$slave'\n";

  ($master,$slave);
}
===========cut here============

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (01/18/90)

In article <3196@taux01.UUCP> arielf@taux01.UUCP (Ariel Faigon) writes:
: --- Question no. 1:
: Is there a simple way to connect a perl program to both sides of a process ?
: i.e. I would love to be able to do something like:
: 
: 	open(OUTPTY,"| some_program |",INPTY) ...;
: 
: pipe input into 'some_program'
: and read its output in order to post-process it.

No.  The only ways to do that currently involve using either sockets, fifos,
or ptys.

And the above syntax, if implemented, wouldn't use a pty anyway.  It would
probably use a socket pair, and you'd be responsible to use select to
avoid deadlock.

: Of course, suppose 'some_program' is "interactive" in nature and the
: 'pipes' are actually pseudo-ttys (otherwise, a deadlock between perl
: & 'some_program' is likely to occur). So I assume I can write to
: 'some_program' and read its immediate "reply" alternately.

A big assumption.  What if it takes 2 seconds?

: This brings me to:
: 
: --- Question no. 2:
: Can I make 'some_program' that is connected to Perl, think it is writing into
: a pty instead of a pipe (actually what I need is that 'some_program' _output_
: be unbuffered - note, I'm not asking about unbuffering perl-output which
: is simple).

Perl can't make some other program do unbuffered (or line-buffered) output
without the cooperation of that process.  For most processes, the only way
to finagle that is by running them under a pty.

: I'm afraid I'll need to write all the 'pty' stuff myself which I
: can do in 'C' in the first place.

But if you write the pty stuff in perl and send it out, you'll be eternally
beloved.  I'd do it if I had the time.

What do you need besides open and ioctl?  And mayby fcntl to set FNDELAY?

Of course, you still have to make sure your protocol is correct.  How
do you know when you've read everything the other program has to say?
How do you keep from getting out of sync?

If you had a reliable way of determining all the processes that were running
on the other end, I suppose you could just wait till they were all blocked,
and then return whatever output you'd collected.

Larry