How can I get command's result code, which executing via ssh


#1

I use net/ssh library.
My code:
require ‘rubygems’
require ‘net/ssh’
Net::SSH.start(‘server’, ‘login’) { |ssh| ssh.open_channel { |ch|
ch.exec(‘false’) { |ch, success| puts success } } }
“false” is a console program and it always return 1.
Why result of this program is “true”? What mean “success” here? I think
I misunderstand something.


#2

On 01.06.2009 12:09, Ivan S. wrote:

I use net/ssh library.
My code:
require ‘rubygems’
require ‘net/ssh’
Net::SSH.start(‘server’, ‘login’) { |ssh| ssh.open_channel { |ch|
ch.exec(‘false’) { |ch, success| puts success } } }
“false” is a console program and it always return 1.
Why result of this program is “true”? What mean “success” here? I think
I misunderstand something.

This is likely because SSH does not propagate the result of the remote
process. You can find the details on SSH’s manpage.

One way would be to send something like

echo “$?”

as last command and read this.

Kind regards

robert


#3

Robert K. wrote:

This is likely because SSH does not propagate the result of the remote
process. You can find the details on SSH’s manpage.

Ahem??

‘man ssh’ on my system (Ubuntu Hardy) says:

 ssh exits with the exit status of the remote command or with 255 if 

an
error occurred.

A quick test confirms this to be true. So clearly the exit status is
propagated back. More likely it’s just a problem with the OP’s use of
the Net::SSH API.

Now, the Net::SSH API documentation (*) for exec tells you:

“success means that the command is being executed, not that it has
completed”

And if it hasn’t completed, obviously you can’t get the exit status. I
admit the documentation doesn’t make it easy to see at a glance how you
should get the exit status.

But in any case, I think it’s not helpful to post unverified guesses
about the behaviour of such a system.

Regards,

Brian.

(*) http://net-ssh.rubyforge.org/ssh/v2/api/index.html


#4

One other point. It’s very dangerous to do

ch.foo { |ch| ... }

because (in ruby 1.8) ‘ch’ is bound to the same variable in both cases.
That is, the value assigned to the block parameter also changes the
outer ‘ch’. So you should call them something different.

The finished program becomes:

require ‘rubygems’
require ‘net/ssh’
Net::SSH.start(‘localhost’, ‘yourname’) { |ssh|
ssh.open_channel { |chan|
chan.on_request(‘exit-status’) { |ch, data|
puts “process terminated with exit status: #{data.read_long}”
}
chan.exec(‘false’) { |ch, success| puts success }
}
}


#5

A quick check of the SSH protocol spec in RFC 4254 says, in section
6.10:

When the command running at the other end terminates, the following
message can be sent to return the exit status of the command.
Returning the status is RECOMMENDED. No acknowledgement is sent for
this message. The channel needs to be closed with
SSH_MSG_CHANNEL_CLOSE after this message.

The client MAY ignore these messages.

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "exit-status"
  boolean   FALSE
  uint32    exit_status

(and it then goes on to describe the “exit-signal” message as well)

Grepping for exit-status in the net-ssh source turns up an example of
how to get it:

# * "exit-status" : the exit status of the remote process will be 

reported
# as a long integer in the data buffer, which you can grab via
# data.read_long.
# * “exit-signal” : if the remote process died as a result of a
signal
# being sent to it, the signal will be reported as a string in the
# data, via data.read_string. (Not all SSH servers support this
channel
# request type.)
#
# channel.on_request “exit-status” do |ch, data|
# puts “process terminated with exit status: #{data.read_long}”
# end


#6

On Mon, Jun 1, 2009 at 3:09 AM, Ivan S. removed_email_address@domain.invalid wrote:

I use net/ssh library.
My code:
require ‘rubygems’
require ‘net/ssh’
Net::SSH.start(‘server’, ‘login’) { |ssh| ssh.open_channel { |ch|
ch.exec(‘false’) { |ch, success| puts success } } }
“false” is a console program and it always return 1.
Why result of this program is “true”? What mean “success” here? I think
I misunderstand something.

I just ran into the same problem last week and I ended up doing what
Robert suggested:

retcode = ch.exec(‘my_command >/dev/null 2>&1 ; echo $?’)

you can also do things like is:
results = ch.exec(‘my_command && echo SUCCESS || echo FAILURE’)


#7

On 01.06.2009 14:48, Brian C. wrote:

But in any case, I think it’s not helpful to post unverified guesses
about the behaviour of such a system.

I am pretty sure that the ssh on at least one system we have at work
does not return the remote command’s exit status. This is not a guess
but I cannot verify it at the moment now because I do not have access to
the system right now.

robert


#8

Brian C. wrote:

One other point. It’s very dangerous to do

ch.foo { |ch| ... }

because (in ruby 1.8) ‘ch’ is bound to the same variable in both cases.
That is, the value assigned to the block parameter also changes the
outer ‘ch’. So you should call them something different.

We use 1.9

The finished program becomes:

require ‘rubygems’
require ‘net/ssh’
Net::SSH.start(‘localhost’, ‘yourname’) { |ssh|
ssh.open_channel { |chan|
chan.on_request(‘exit-status’) { |ch, data|
puts “process terminated with exit status: #{data.read_long}”
}
chan.exec(‘false’) { |ch, success| puts success }
}
}

Thanks! It’s what I need!


#9

Robert K. wrote:

On 01.06.2009 14:48, Brian C. wrote:

But in any case, I think it’s not helpful to post unverified guesses
about the behaviour of such a system.

I am pretty sure that the ssh on at least one system we have at work
does not return the remote command’s exit status. This is not a guess
but I cannot verify it at the moment now because I do not have access to
the system right now.

You said: “SSH does not propagate the result of the remote process. You
can find the details on SSH’s manpage”, but a check of the manpage says
otherwise.

Of course, perhaps older versions of ssh are different. However the
system I checked on was Ubuntu Hardy, which isn’t exactly bleeding edge.