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

I am using ssh to start to open a connection to a "service" account on a remote server. The remote account is configured to automatically start a process which reads a few lines (three, right now), processes the information and returns a status. That part works.

On the client side I am testing with:

use IPC:;Open2; my ($childin,$childout,$childerr); my $cmd = qq(/usr/bin/ssh -i $KEYFILE $USER\@$SERVER 2>/dev/null); my $pid = open2 ($childout,$childin,$cmd); my $status = $!; # check for success here # send commands print $childin $line1 print $childin $line2 print $childin $line3 etc...

That works but if there is a "better" way I'd like to know. I tried with Net::SSH::Perl but couldn't get it to work with the remote server starting the command. Maybe I am overlooking something.

The application is intended to allow a user to reset and change his or her Kerberos password from a web interface. The web server does not run on the Authentication Server or KDC.

Replies are listed 'Best First'.
Re: Connecting to STDIN and STDOUT over SSH
by almut (Canon) on Apr 09, 2009 at 20:52 UTC
    That works but if there is a "better" way I'd like to know.

    What could be better than a working solution? :)

    Ok, seriously, I don't see a problem with doing it that way. And just in case you're worried this might be slower than a Net::SSH::Perl solution (had you gotten it to work), let me reassure you that it's most likely the other way round, despite the 'overhead' of calling an external binary...

      What could be better than a working solution? :)

      When you put that way ... :-)

      Speed is not a particular issue. This is a web page and will probably get a few dozen hits month. A few extra milliseconds are not a problem.

      Thank you for the advice.

Re: Connecting to STDIN and STDOUT over SSH
by ikegami (Patriarch) on Apr 10, 2009 at 03:12 UTC

    I see three maybe four problems. The shell is needless invoked, ssh's arguments aren't properly converted into shell literals, $status contains junk, and you might want to intercept SIGPIPE.

    • The shell is needless invoked. You can avoid it as follows:

      my @cmd = ( '/usr/bin/ssh', '-i', $KEYFILE, "$USER\@$SERVER" ); open($fh_null, '>', '/dev/null') or die("open /dev/null: $!\n"); my $pid = open3($childin, $childout, '>&'.fileno($fh_null), @cmd);
    • ssh's arguments aren't properly converted into shell literals as far as I can tell. If you avoid the shell as shown above, it becomes moot. You'll be able to safely pass $KEYFILE, $USER and $SERVER.

    • $status contains junk. $! is only meaningful after a system call that returned an error, yet you read it unconditionally.

      Furthermore, open2() "doesn't return on failure: it just raises an exception matching /^open2:/". You'll never actually reach that line if there's a problem. If you want to catch errors from open2 or open3, you'll need to use eval { }.

    • Finally, if the child dies prematurely, you program will receive a SIGPIPE and die unless you intercept that signal (e.g. $SIG{PIPE} = 'IGNORE'; or $SIG{PIPE} = \&handler;).

Re: Connecting to STDIN and STDOUT over SSH
by salva (Canon) on Apr 13, 2009 at 07:57 UTC
    If you don't need it to work on Windows, use Net::OpenSSH:
    my $ssh = Net::OpenSSH->new($SERVER, user => $USER, master_opts => [-i => $KEYFILE], master_stderr_discard => 1); $ssh->error and die "unable to connect to remote host: " . $ssh->error +; my $out = $ssh->capture({stdin_data => $lines});