Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Re^2: Ssh and qx

by cbeckley (Curate)
on Mar 31, 2017 at 08:57 UTC ( #1186598=note: print w/replies, xml ) Need Help??


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

I admit I never tested piped opens, I was told by somebody who knows Perl far better than I that they were not available in 5.004, and I didn't question it. Thank you for pointing that out.

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?

Thank you for your corrections.

Thanks,
cbeckley

Replies are listed 'Best First'.
Re^3: Ssh and qx
by afoken (Canon) on Mar 31, 2017 at 17:21 UTC
    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.

    Update:

    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.

    Alexander

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

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (4)
As of 2020-06-07 10:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you really want to know if there is extraterrestrial life?



    Results (42 votes). Check out past polls.

    Notices?