Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Re^3: Ssh and qx

by afoken (Canon)
on Mar 31, 2017 at 17:21 UTC ( #1186630=note: print w/replies, xml ) Need Help??

in reply to Re^2: Ssh and qx
in thread Ssh and qx

Regarding my use of $!, I only access it when the output is undefined, which I thought happened when something goes wrong with starting ssh. Is that not the case?

Yes, you are right, sorry. I initially misread your code (my brain just ignored the defined), but then I corrected that. Or so I thought, I forgot the $! part. The naming is still problematic, because it's not an ssh problem, it's a problem starting ssh.

I admit I never tested piped opens

Oh yes you did, implicitly in every `` and in every qx. "Unsafe" pipe opens are the more verbose variant of qx. I think you could replace qx with a function similar to this one:

sub inefficient_qx { my $cmd=shift; local *PIPE; open PIPE,"$cmd |" or die "Could not open pipe from $cmd"; # ^-- intentionally written using an old-style bareword handle +for ancient perls if (wantarray) { my @tmp=<PIPE>; close PIPE or die "Close pipe failed: $!"; return @tmp; } else { my $tmp=do { local $/=<PIPE> }; close PIPE or die "Close pipe failed: $!"; return $tmp; } }

The unsafe parts:

  • relying on the shell (or, to be more precise, one of a thousand different shells randomly installed as /bin/sh) to parse $cmd
  • not dropping permissions when running as root

"Safe pipe opens" drop privileges, that's the ($EUID, $EGID) = ($UID, $GID) part. And, "safe pipe opens" use the list form of exec, avoiding shell issues.

This was the only way (except for manually messing with pipes) to do it until perl 5.8.0 arrived. Perl 5.8.0 extends the three-argument-form of open (open $handle, $mode, $filename) to accept a list of command and arguments in place of $filename when $mode is either "-|" or "|-" (open $handle, $mode, @list). This way, you could get a moderately safe variant of qx, but unfortunately, if @list has exactly one element, perl starts guesswork with that one element:

> perl -E 'open my $pipe,"-|","pstree --ascii --arguments --long $$ 1> +&2" or die $!;' perl -E open my $pipe,"-|","pstree --ascii --arguments --long $$ 1>&2" + or die $!; `-sh -c pstree --ascii --arguments --long 22176 1>&2 `-pstree --ascii --arguments --long 22176 >

If the one-element list would not be treated specially, perl would complain that it could not find an executable with that funny name. This can be seen by incrementing the list size to 2:

> perl -E 'open my $pipe,"-|","pstree --ascii --arguments --long $$ 1> +&2","dummy" or die $!;' No such file or directory at -e line 1. >

So, despite the elegant call, three-argument pipe open still messes with the shell, and you should really use the "safe pipe opens" code from perlipc. If your script runs with elevated privileges (e.g. running as root or setuid/setgid), there is no other way to drop privileges.


The special treatment of a single-element list is, of course, not specific to more-than-two-arguments pipe open. system and exec show the same behaviour:

> perl -E 'system("pstree --ascii --arguments --long $$ 1>&2")==0 or d +ie $!' perl -E system("pstree --ascii --arguments --long $$ 1>&2")==0 or die +$! `-sh -c pstree --ascii --arguments --long 20620 1>&2 `-pstree --ascii --arguments --long 20620 > perl -E 'system("pstree --ascii --arguments --long $$ 1>&2","dummy") +' alex@enterprise pts/0 16:09:53 /home/alex>perl -E 'system("pstree --ascii --arguments --long $$ 1>&2" +,"dummy")==0 or die $!' No such file or directory at -e line 1. > perl -E 'exec("pstree --ascii --arguments --long $$ 1>&2") or die $! +' sh -c pstree --ascii --arguments --long 20657 1>&2 `-pstree --ascii --arguments --long 20657 > perl -E 'exec("pstree --ascii --arguments --long $$ 1>&2","dummy") o +r die $!' No such file or directory at -e line 1. >

But unlike open, system and exec have a workaround. Specify the real executable as indirect object and the shell magic is gone:

> perl -E '@list=("pstree --ascii --arguments --long $$ 1>&2"); system + { $list[0] } @list and die $!' No such file or directory at -e line 1. > perl -E '@list=("pstree --ascii --arguments --long $$ 1>&2"); exec { + $list[0] } @list or die $!' No such file or directory at -e line 1. >

This is documented in exec and shorter also in system. Pipe open lacks this functionality / workaround, and you have to resort to the quite long code from "Safe pipe open" in perlipc.


Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1186630]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (5)
As of 2022-05-16 09:57 GMT
Find Nodes?
    Voting Booth?
    Do you prefer to work remotely?

    Results (62 votes). Check out past polls.