Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

How do you properly tty-ify a non-tty STDOUT?

by ph713 (Pilgrim)
on Sep 22, 2004 at 15:52 UTC ( [id://392942]=perlquestion: print w/replies, xml ) Need Help??

ph713 has asked for the wisdom of the Perl Monks concerning the following question:

I'm getting to a point of frustration with a rather simple concept, so I seek the wisdom of those that spend more time with perl than I do. The basic idea is that my perl script's STDOUT is not a tty (reasons unimportant), and my perl script is going to call an external program via system(), which checks to see if STDOUT is a tty (using isatty()) and changes its behavior accordingly. I want to fake the external program out and convince it that STDOUT is a tty, when it is in fact not, but I still want the output to end up going to my STDOUT.
Here's code from my last attempt to get this to work, which I think will probably illuminate whatever it is I'm failing to understand about pseudo-ttys and perl filehandles and whatnot. It attempts to create a pseudo-tty, and then use fdopen to "inject" the pseudo-tty into the path of STDOUT traffic to make STDOUT look like a tty:
use IO::Pty; use IO::Handle; open(REAL_STDOUT,">&STDOUT"); my $pty = new IO::Pty; my $slave = $pty->slave; STDOUT->fdopen($slave,'w') || die $!; $pty->fdopen(\*REAL_STDOUT,'w') || die $!; system("echo This is stdout output from an external program");
/Edited to add more... Round 2: Some more investigation and now I'm at this solution (stolen from some webpage somewhere and hacked up). This seems to get the output through the pty, but the while/print loop at the bottom never terminates, even though the process writing to the filehandle in question is long gone...
use IO::Handle; use IO::Pty; + + + sub do_cmd() { my $pty = new IO::Pty; defined (my $child = fork) or die "Can't fork: $!"; return $pty if $child; + POSIX::setsid(); my $slave = $pty->slave; close($pty); STDOUT->fdopen($slave,'>') || die $!; STDERR->fdopen(\*STDOUT,'>') || die $!; system("echo This is stdout output from an external program"); exit 0; } + my $fh = do_cmd(); while(<$fh>) { print; }

Replies are listed 'Best First'.
Re: How do you properly tty-ify a non-tty STDOUT?
by sgifford (Prior) on Sep 22, 2004 at 17:41 UTC
    The problem with your second example seems to be that the child process (running the while) still has the slave file descriptor opened. Closing the slave file descriptor it in the child process fixes it for me; I changed return $pty if $child; to:
    if ($child) { $pty->close_slave(); return $pty; }
    See this note in the IO::Pty documentation:
    close_slave()
    The slave filehandle will be closed and destroyed. This is necessary in the parent after forking to get rid of the open filehandle, otherwise the parent will not notice if the child exits. Subsequent calls of slave() will return a newly opened slave filehandle.

    Also, it's customary to have the child start up a new process and have the parent continue the main flow of execution; your way will work, but isn't the normal way of doing things. And if you're using system(...); exit(0); in your actual program (and not just this sample code), consider using exec instead.

    Update: Yup, I misread ph713's code; it forks in a perfectly normal way.

      AHA! Thanks, you're right, the close_slave() thing was what I was missing. And no, the sample code looks nothing like the real script, this was just an isolated chunk of test code to work with this problem and make posting here easier :) Thanks again!
      By the way (and I could be confused, my brain has been dragged through the mud today a few times) - the forking sample code actually does have the parent continuing the flow of execution, with the child doing the system();exit();. The return $pty if $child; line looks in english like it's the other way around, but the parent gets $child defined as a pid number, and the actual child gets $child returned as zero. Therefore the idiom $child = fork; XXX if $child; is very ugly, as the perl meaning is the opposite of the english meaning. Don't blame me, I stole it from an example in a web copy of Chapter 6 of "Network programming with Perl". :)
Re: How do you properly tty-ify a non-tty STDOUT?
by etcshadow (Priest) on Sep 22, 2004 at 19:14 UTC
Re: How do you properly tty-ify a non-tty STDOUT?
by borisz (Canon) on Sep 22, 2004 at 16:08 UTC
    Try Expect as far as I know it fools other tool's well.
    Boris
      Yes, I strongly suspect that Expect could be made to do the job, but it's a very inelegant way of getting it done, seeing as I don't plan on piping any input into the command or parsing the output or anything of the sort that Expect does. I really just want to do system("somecmd"), and no more functionality than that - except I need to convince somecmd that it is talking to a tty (which it isn't). It seems Expect is using a similar approach (IO::Pty), and I'm sure my solution lies within the Expect.pm source code, I just don't see it yet :)
Re: How do you properly tty-ify a non-tty STDOUT?
by ph713 (Pilgrim) on Sep 23, 2004 at 20:22 UTC
    Just for the record, here's the final code from the actual script it's being used in, which works correctly. This subroutine forks off an external command and returns a filehandle that you can read and write to, and convinces the external program that it's running in a terminal.
    use IO::Handle; use IO::Pty; use POSIX; sub forkptycmd($) { my $cmd = shift; my $pty = new IO::Pty; my $pid = fork; if(!defined($pid)) { die "error forking: $!"; } if($pid==0) { POSIX::setsid(); my $slave = $pty->slave; close($pty); STDOUT->fdopen($slave,'>') || die $!; STDIN->fdopen($slave,'<') || die $!; STDERR->fdopen(\*STDOUT,'>') || die $!; close($slave); exec($cmd); } $pty->close_slave(); return $pty; }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://392942]
Approved by Paladin
Front-paged by Tomte
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (3)
As of 2024-04-24 22:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found