tags:

views:

72

answers:

2

Here is this line in perl:

open my $fh_echo, '-|' or exec "$sshstr \"$str\"";

Basically it exec's $sshstr and then the output goes into $fh_echo, somehow. I am curious as to the mechanisms that go behind this operation. Can someone explain what the '-|' means?

Also how does this loads the output into $fh_echo? Does it output it little by little or is $fh_echo simply a handle to the output of exec?

Haha I can't even google this cuz -| don't show up in the results.

+4  A: 

See perlfork. That's a forking pipe open.

Basically, |- pipes output to a forked child process, and -| reads (within the child process) from the parent.

From perldoc -f open:

If you open a pipe on the command '-', i.e., either '|-' or '-|' with 2-arguments (or 1-argument) form of open(), then there is an implicit fork done, and the return value of open is the pid of the child within the parent process, and 0 within the child process.

In your example, you're opening a pipe to read from a child process, then forking, then in the child, replacing the process with $sshstr.

Pedro Silva
Oh so basically it grabs output from the child process, which is exec and sets $fh_echo as a handle to that output? Is this a buffered process? Or is it synchronous?
Razor Storm
It is a buffered handle, so you'll need to set `$|`.
Pedro Silva
open() just always fails in this example, because you need to provide a right-side. So that means the above code always exits by calling exec(qq[$sshstr "$str"]).
Scott S. McCoy
@Scott S. McCoy: incorrect. open forks and returns true (a pid) in the parent and false in the child, so only the child does the exec, with stdin coming from the parent's $fh_echo filehandle.
ysth
@Scott S. McCoy: in your linked code, I think the die is confusing the results, but the doc says "If you open a pipe on the command '-', i.e., either '|-' or '-|' with 2-arguments (or 1-argument) form of open(), then there is an implicit fork done, and the return value of open is the pid of the child within the parent process, and 0 within the child process." And the doc is correct.
ysth
@ysth — yeah, my perl-foo got owned.
Scott S. McCoy
+2  A: 

It seems this does the same thing as open my $fh_echo, "-|", "$sshstr \"$str\"". It deparses as:

exec qq[$sexec qq[$sshstr "$str"] unless open my $fh_echo, '-|'

The output isn't really loaded into $fh_echo when things are opened successfully, $fh_echo becomes a filehandle with the output of the executable in the right-most argument of open() by way of a pipeline. For more on pipelines, check out the wikipedia page. It should be pointed out that your given example, however, doesn't open anything. Because there is no command on the right-hand side of the example you provided, open() will always return a non-true value indicating failure, and the right side of the expression will always run. See http://ideone.com/wupJ8

To go into further details if it were to be successful, we'll use open my $f, "-|", "echo hi" as a better example. What this does is redefine stdin on for the child process as being the right side of a pipe, making it roughly equivalent to the following:

pipe my $fh_echo, my $childout;

if (fork == 0) {
    close STDOUT; 
    open STDOUT, ">>&", $childout;

    exec "echo hi";
}

This first creates a pipe, and then forks. In the child process, stdout is redefined as the write side of the pipe. exec is then called (see exec(2) for more details) which replaces the current process with the given process. From that point on, since stdout has been redefined as being the writable side of the pipe, all output to stdout will actually write to the pipe instead of what-ever the target might have been before.

Also, to go into further detail on exec "echo hi" defects to a shell since it's not a fully qualified executable path. Which is to say, it actually gets read by perl as exec "/bin/sh", "echo hi" since perl decides to make no decisions as to what the given expression might really mean.

Hopefully that explains the mechanics.

Scott S. McCoy
Haha very detailed explanation, except for the tiny thing pointed out by the comments on the other answer. :P Regardless, thanks! Basically I have this line in my code to feed back the results of the $str script in AJAX calls. However, due to buffering and random issues, this sometimes times out the cgi script, so I was wondering the mechanisms so I may tweak it around. However, in retrospect I most likely just have to change the AJAX headers to allow continuous requests, or at the least lengthen the expire time.
Razor Storm
Hopefully you aren't dynamically building $sslstr or $str and then using it to make a syscall in a CGI script. That would be very dangerous.
Scott S. McCoy
Well.. I partially am. However, I do have a lot more securities involved in that piece of code that I left out of my snipplet.
Razor Storm