IO::Pty - reads/writes fail *only* with system()

IO::Pty - reads/writes fail *only* with system()

am 10.01.2006 13:59:07 von Alexis Huxley

Starting from the example in the IO::Pty man page I've now got a
perl script which forks, the child reopens stdin/stdout on the
slave side of the pty, and the parent reopens on the master side.

The child writes to STDOUT using 'print', the parent reads from
STDIN using 'while (<>) ...'. This works fine.

I changed the child to write using 'system("echo ...")' and later
I changed the parent to read using 'sed' or 'cat'. This fails,
and I can't see why.

Using only the child using 'system()' the output simply never
appears on the parent side. Using only the parent using 'system()'
results in:

sed: read error on stdin: Bad file descriptor

I tried various combinations of the set_raw() and setting the
controlling terminal of the child using the documented methods,
but still no dice. Googling brings up plenty of pre-OO examples
of pty but nothing besides what's in the FAQ and the perlipc
man page.

Judging by the sed error message, I guess somehow my plumbing
of file handles has gone wrong, but I just can't see it, especially
as it works when - as mentioned above - I use 'print' for the
output and 'while (<>) ..." for the input. Has anybody any ideas?

The script is short and reproduced below. As it is it works, but
with either of the current write/read lines replaced by the
system() equivalents, it will fail:

#!/usr/bin/perl

use IO::Pty;
$pty = new IO::Pty;
$slave = $pty->slave;
(defined($pid = fork)) || die "fork failed";


# Child
if ($pid == 0) {
$pty->make_slave_controlling_terminal() || die;
close($pty);

# Remap stdin and stdout to slave side of pty
close(STDOUT);
open(STDOUT, ">&=" . $slave->fileno) || die;
close(STDIN);
open(STDIN, "<&=" . $slave->fileno) || die;

# -- write options: print works, system doesn't!
print "To be or not to be, that is the question.\n";
#system("echo \"To be or not to be, that is the question.\"");

# Tidy up
close($slave);

# Parent
} else {
$pty->close_slave();

# Remap stdin and stdout to master side of pty
close(STDOUT);
open(STDOUT, ">&=" . $pty->fileno) || die;
close(STDIN);
open(STDIN, "<&=" . $pty->fileno) || die;

# -- read options: print (<>), system doesn't!
while (<>) { chomp; print STDERR "parent read from slave: $_\n"; }
#system("sed 's/^/parent read from slave: /'");

# Tidy up
close($pty);
}

In case it's relevent:

This is perl, v5.8.4 built for i386-linux-thread-multi, running on
Debian Linux.

(Ultimately what I'm trying to do is rewrite a script I googled called
ssh-ppp, which runs 'ssh pppd' in one half, and 'pppd' in
the other. Yes, I know that pppd has a 'pty' directive, but that just
doesn't cut the mustard for what I want ,which is to do
'su -c "ssh -t ssh -t slogin "'
where my shell on is pppd, and all hops require inputting
password, and avoiding entering passwords by using RSA authentication
is neither possible nor desirable.)

Thanks for reading so far :-) and any advice you can offer; the
results of this labour will be google-able.

Alexis

Re: IO::Pty - reads/writes fail *only* with system()

am 10.01.2006 15:16:13 von unknown

Alexis Huxley wrote:
> Starting from the example in the IO::Pty man page I've now got a
> perl script which forks, the child reopens stdin/stdout on the
> slave side of the pty, and the parent reopens on the master side.
>
> The child writes to STDOUT using 'print', the parent reads from
> STDIN using 'while (<>) ...'. This works fine.
>
> I changed the child to write using 'system("echo ...")' and later
> I changed the parent to read using 'sed' or 'cat'. This fails,
> and I can't see why.
>
> Using only the child using 'system()' the output simply never
> appears on the parent side. Using only the parent using 'system()'
> results in:
>
> sed: read error on stdin: Bad file descriptor
>
> I tried various combinations of the set_raw() and setting the
> controlling terminal of the child using the documented methods,
> but still no dice. Googling brings up plenty of pre-OO examples
> of pty but nothing besides what's in the FAQ and the perlipc
> man page.
>
> Judging by the sed error message, I guess somehow my plumbing
> of file handles has gone wrong, but I just can't see it, especially
> as it works when - as mentioned above - I use 'print' for the
> output and 'while (<>) ..." for the input. Has anybody any ideas?
>
> The script is short and reproduced below. As it is it works, but
> with either of the current write/read lines replaced by the
> system() equivalents, it will fail:
>
> #!/usr/bin/perl
>
> use IO::Pty;
> $pty = new IO::Pty;
> $slave = $pty->slave;
> (defined($pid = fork)) || die "fork failed";
>
>
> # Child
> if ($pid == 0) {
> $pty->make_slave_controlling_terminal() || die;
> close($pty);
>
> # Remap stdin and stdout to slave side of pty
> close(STDOUT);
> open(STDOUT, ">&=" . $slave->fileno) || die;
> close(STDIN);
> open(STDIN, "<&=" . $slave->fileno) || die;
>
> # -- write options: print works, system doesn't!
> print "To be or not to be, that is the question.\n";
> #system("echo \"To be or not to be, that is the question.\"");

How about

print `echo "To be or not to be, what was the question?"`;

>
> # Tidy up
> close($slave);
>
> # Parent
> } else {
> $pty->close_slave();
>
> # Remap stdin and stdout to master side of pty
> close(STDOUT);
> open(STDOUT, ">&=" . $pty->fileno) || die;
> close(STDIN);
> open(STDIN, "<&=" . $pty->fileno) || die;
>
> # -- read options: print (<>), system doesn't!
> while (<>) { chomp; print STDERR "parent read from slave: $_\n"; }
> #system("sed 's/^/parent read from slave: /'");

How about

open SED, "|sed 's/^parent read from slave: /'" or die;
while (<>) {print SED}

But I'm not sure what sed does for you that Perl won't. My sed is really
rusty, but I suspect your sed is the equivalent of command different from

while (<>) {s/^/parent read from slave: /; print}

and I suspect that anything you wanted to do to the input stream could
be done just as well with Perl.

>
> # Tidy up
> close($pty);
> }
>
> In case it's relevent:
>
> This is perl, v5.8.4 built for i386-linux-thread-multi, running on
> Debian Linux.
>
> (Ultimately what I'm trying to do is rewrite a script I googled called
> ssh-ppp, which runs 'ssh pppd' in one half, and 'pppd' in
> the other. Yes, I know that pppd has a 'pty' directive, but that just
> doesn't cut the mustard for what I want ,which is to do
> 'su -c "ssh -t ssh -t slogin "'
> where my shell on is pppd, and all hops require inputting
> password, and avoiding entering passwords by using RSA authentication
> is neither possible nor desirable.)
>
> Thanks for reading so far :-) and any advice you can offer; the
> results of this labour will be google-able.
>
> Alexis

The basic problem is that just kicking off a system command doesn't
necessarily magically connect the command's STDIN and STDOUT to yours.

Tom Wyant

Re: IO::Pty - reads/writes fail *only* with system()

am 10.01.2006 15:27:21 von Alexis Huxley

>> Starting from the example in the IO::Pty man page I've now got a
>> perl script which forks, the child reopens stdin/stdout on the
>> slave side of the pty, and the parent reopens on the master side.
>> ...
>
> print `echo "To be or not to be, what was the question?"`;
>
> How about
>
> open SED, "|sed 's/^parent read from slave: /'" or die;
> while (<>) {print SED}
>
>> ...
>> (Ultimately what I'm trying to do is rewrite a script I googled called
>> ssh-ppp, which runs 'ssh pppd' in one half, and 'pppd' in
>> the other. Yes, I know that pppd has a 'pty' directive, but that just
>> doesn't cut the mustard for what I want ,which is to do

Maybe I wasn't clear enough; the echo and sed commands are not what I
want to do *ultimately*; they will be replaced by calls to ssh and to
pppd respectively. But I want to start simple.

> The basic problem is that just kicking off a system command doesn't
> necessarily magically connect the command's STDIN and STDOUT to yours.

Yes, as I said, I think my plumbing is somehow wrong. But still, these
handles are generally inherited otherwise the following command would
produce some output, which it doesn't:

sh -c 'echo hello >&2' 2>/dev/null

and that being so, my the error in my script isn't obvious to me; hence
asking, specifically, what did I do wrong? THanks!

Alexis