3

Here is what I have- A CGI script on server A gets the parameters entered in a webform, creates a remote ssh command that calls another perl file on server B and passes all parameters as arguments to the ash command. Perl file on server B parses arguments n operates thereafter.

Now since I won't have all parameters populated at all times, I am passing a character such as "=" appended to each parameter from CGI on server A and before processing on Server B, I chop that last character which is an "=".

However, this fails for a parameter that can have space. To counter this, I can enclose each parameter within ///" (basically a slash to escape " and then a slash to escape the other slash) before I append the "="(which probably can be discarded once I enclose each param anyways), but is this the best way to do what I want to achieve?

Prasoon
  • 425
  • 1
  • 6
  • 18

3 Answers3

6

Rather than calling the command and reading its output, I'd suggest using IPC::Open2 or possibly IPC::Open3 and using your process B as a filter: write in, read out. At this point, you completely eliminate the shell. And, in my experience, eliminating the shell is always a good thing.

And then, to make things really simple, you run the command via IPC::Open[23], write serialised data to it (via JSON or Storable), close the write pipe (so the other side gets eof), and wait for the data. At the other end, read stdin, deserialise it, use it, and serialise your return data again. Back at the CGI server, deserialise the data you received, and away you go. This also works really well if you need to do double or triple bouncing (ssh through one machine to ssh to another).

It is possible to put in enough quotes to get it to work the way you want. It's moderately painful. However, here are some hints in that direction. First, find something on CPAN which handles the quoting for you. String::ShellQuote might work. Second, avoid the shell as much as possible. That is, instead of using open my $pipe, "ssh $server '$cmd' |", use open my $pipe, '-|', 'ssh', $server, $cmd. This will avoid the local shell. And that reduces the number of times you have to worry about quoting anything. But you will have to re-quote everything every time you do another bounce as each remote machine will still be using the remote shell.

Tanktalus
  • 21,664
  • 5
  • 41
  • 68
3

Use Net::OpenSSH, it will take care of the quoting for you:

use Net::OpenSSH;

my $ssh = Net::OpenSSH->new($host);
my ($out, $err) = $ssh->capture2($cmd, @args);
salva
  • 9,943
  • 4
  • 29
  • 57
1

I use code such as

sub text_to_shell_lit(_) {
   return $_[0] if $_[0] =~ /^[a-zA-Z0-9_\-]+\z/;  # Optional
   for (my $s = $_[0]) {
      utf8::downgrade($_);  # Check for non-bytes
      die if /\0/;          # Check for NUL
      s/'/'\\''/g;
      return "'$_'";
   }
}

my $remote_cmd = join ' ', map text_to_shell_lit,
   perl => ( '-e' => $perl_code, '--', @args );

my @ssh_cmd = (ssh => ( '--', $remote_target, $remote_cmd ));
   -or-
my $ssh_cmd = join ' ', map text_to_shell_lit,
   ssh => ( '--', $remote_target, $remote_cmd );

But you could use String::ShellQuote's shell_quote instead of text_to_shell_lit.

use String::ShellQuote qw( shell_quote );

my $remote_cmd = shell_quote
   perl => ( '-e' => $perl_code, '--', @args );

my @ssh_cmd = (ssh => ( '--', $remote_target, $remote_cmd ));
   -or-
my $ssh_cmd = shell_quote
   ssh => ( '--', $remote_target, $remote_cmd );
ikegami
  • 367,544
  • 15
  • 269
  • 518