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

Hi Monks-

I'm trying to run a remote command (via Net::SSH::Perl) that never returns, and would like to be able to break out of it whenever I want.

How can I do this?

Below is some sample code I've written to illustrate what I want to do.

In the alarm handler, I would like to break the currently running command (close doesn't work), then re-run the command to start counting again.

Any pointers are much appreciated!

Thanks

-Craig

Update: Searching through the code, I see that $ssh->_disconnect is available, but it is a null subroutine. Still looking...

Update 2: Using $ssh->break_client_loop seems to work. Not sure of side-effects yet...

use strict; use warnings; use Net::SSH::Perl; my $myssh; $SIG{ALRM} = sub { print STDERR "\nALARM\n"; $myssh->close; ### There is no close - what can I use? remcount($myssh); alarm 5; }; $myssh = Net::SSH::Perl->new( "MY.REMOTE.SERVER", protocol => 2 ); alarm 5; remcount($myssh); sub remcount { my $ssh = shift || die "missing ssh object"; $ssh->login("MYLOGIN", "MYPASSWORD") or die "Failed to login: $!\n +"; # STDOUT handler... $ssh->register_handler('stdout', sub { my ($channel, $buffer) = @_; print STDERR "OUT BYTES: ", $buffer->bytes, "\n"; }); my $perlcmd = join('', <DATA>); my $cmd = "/bin/perl - -heartbeat "; print STDERR "REMOTE: $cmd\n"; my ($stdout, $stderr, $exit) = $ssh->cmd($cmd, $perlcmd); } __DATA__ use strict; use warnings; $|++; my $c=0; while(1) { print $c++; sleep 1; }

Replies are listed 'Best First'.
Re: Net::SSH::Perl - Breaking out of a running command?
by jellisii2 (Hermit) on Aug 06, 2014 at 11:43 UTC
    I would look at use threads; (perlthrtut) and study how the semaphores work, with the basic idea that the connection is run in a separate thread that is managed by your program. I have not had to manage semaphores before, so I have no experience to help guide you on this.
      Thanks for the suggestion, but I don't have the option of using threads in the environment that this runs in.
METHOD1: Using break_client_loop() & undef($ssh)
by cmv (Chaplain) on Aug 06, 2014 at 15:23 UTC
    Folks-

    I believe I've solved this issue (but still would like to have some more experienced folks comment on this solution).

    Here is the code that seems to do what I want - critiques welcome!

    Thanks

    -Craig

    Title Update: - this solution is brute-force. See additional posts in this thread for other techniques.

    Update 2: - Adding $ssh->sock->close() to code. I like this solution because there seems to be no side effects (since you are taking down the entire ssh connection each time). Also, it should work with either SSH1 or SSH2 protocols.

    I don't like this solution because it isn't very elegant or efficient (if you use SSH2, you should keep the ssh connection up and just use a new channel).

    use strict; use warnings; use Net::SSH::Perl; my $ssh; $SIG{ALRM} = sub { print STDERR "\nALARM\n"; #$ssh->close; # There is no close - what can I use? #$ssh->_disconnect; # Null subroutine DOES NOT WORK $ssh->break_client_loop; # Seems to work $ssh->sock->close; }; my $perlcmd = join('', <DATA>); my $cmd = "/bin/perl - -heartbeat "; while(1) { alarm 5; remcount(); undef($ssh); print STDERR "NEXT...\n"; } sub remcount { $ssh = Net::SSH::Perl->new( "MYHOST", protocol=>2 ); $ssh->login("MYLOGIN", "MYPASS") or die "Failed to login: $!\n"; # STDOUT handler... $ssh->register_handler('stdout', sub { my ($channel, $buffer) = @_; print STDERR "OUT BYTES: ", $buffer->bytes, "\n"; }); print STDERR "REMOTE: $cmd\n"; my ($stdout, $stderr, $exit) = $ssh->cmd($cmd, $perlcmd); } __DATA__ use strict; use warnings; $|++; my $c=0; while(1) { print $c++; sleep 1; }
Re: Net::SSH::Perl - Breaking out of a running command?
by Anonymous Monk on Aug 06, 2014 at 21:15 UTC

    Just an idea: can you wrap the remote command in a script on the remote side that does what you want?

    As for your other solution, I can't seem to find any documentation on break_client_loop, so I'm not sure if using an undocumented method is the most portable / robust solution.

      Thanks for the help!

      I'm not sure what I can do on the remote side to break me out of a running command. It seems like what I WANT to do is to control the SSH2 channels from my client side perl script.

      Controlling SSH2 channels seems to be the intent of the software (documented in Net::SSH::Perl::ChannelMgr) but a lot of stuff that should be documented, isn't (like the break_client_loop call, as you pointed out).

      I'm getting farther in understanding the guts of these modules. Hopefully I'll be able to get it right soon.

METHOD2: Using $chan->rcvd_ieof()
by cmv (Chaplain) on Aug 08, 2014 at 18:40 UTC
    Monks-

    Here is my final Net::SSH::Perl example of locally breaking out of a running command on a remote host every 5 seconds, shutting down the old command, then starting up a new one (in a new SSH2 channel).

    It's based upon using $ssh->channel_mgr->{channels} and $chan->rcvd_ieof neither of which are documented, but seem like the right interfaces to be using (comments?).

    I'm thinking of following-up with the module author (Steffen Schwigon) to add and document the following methods to officially support this:

    my @channels = $ssh->listChannels();
    $ssh->closeChannel($channels[0]);

    What do you think?

    Thanks to all those who've helped me out on this issue!

    -Craig

    Title Update - This pretends we got an incoming EOF on the channel. WARNING: if you add a register_handler for stderr (and there is no activity on stderr), it seems to hang. I am investigating...

    Update 2: - I like this solution because it is more efficient and elegant. However, there are too many side-effects, and I haven't the time to solve them all (need to get the author to advise). Here are some of the notes that helped me solve some of the side-effects that I discovered:

    Re^4: Net::SSH::Perl hangs(during cmd), but only in Mason environment
    Re^3: Net::SSH::Perl ConnectTimeout (ssh -o option)

    use strict; use warnings; use Net::SSH::Perl; my $HOST='MYHOST'; my $ID='MYLOGIN'; my $PW='MYPW'; my $DEBUG=0; # 1 - provides ssh debug output my $perlcmd = join('', <DATA>); my $cmd = "/bin/perl - -heartbeat "; # Login to host... my $myssh = Net::SSH::Perl->new( $HOST, protocol=>2, debug=>$DEBUG ); $myssh->login($ID, $PW) or die "Failed to login: $!\n"; listchannels($myssh); # Alarm Handler... $SIG{ALRM} = sub { print STDERR "\nALARM\n"; listchannels($myssh); # Close latest channel (pretend we received an EOF)... my $chanp = getchannels($myssh); $chanp->[-1]->rcvd_ieof; }; # Startup a new command channel on HOST every 5 seconds... while(1) { print STDERR "============================\n"; alarm 5; remcount($myssh); # Add new channel print STDERR "NEXT...\n"; } sub listchannels { my $ssh = shift || die "Missing ssh object"; my $x=0; foreach my $c (@{getchannels($ssh)}) { if($c) { print STDERR "CHAN $x($c->{id}) :$c\n" } else { print STDERR "CHAN $x(X) --CLOSED--\n" }; $x++; } } sub getchannels { my $ssh = shift || die "Missing ssh object"; return($myssh->channel_mgr->{channels}); } sub remcount { my $ssh = shift || die "Missing ssh object"; # STDOUT handler (protocol 2 only)... $ssh->register_handler('stdout', sub { my ($channel, $buffer) = @_; print STDERR "CHAN $channel->{id}: ", $buffer->bytes, "\n"; }); print STDERR "REMOTE: $cmd\n"; my ($stdout, $stderr, $exit) = $ssh->cmd($cmd, $perlcmd); print STDERR "DONE WITH COMMAND!!!\n"; } __DATA__ use strict; use warnings; $|++; my $c='a'; while(1) { print $c++; sleep 1; }
Re: Net::SSH::Perl - Creating multiple SSH2 Channels
by cmv (Chaplain) on Aug 07, 2014 at 14:21 UTC
    Folks-

    As I'm learning about the guts of Net::SSH::Perl, I've discovered a neat way to generate multiple commands running on separate SSH2 channels (and how to pull in the output along with which channel it came from).

    I figured it would be good to post this to help those who are trying to do something similar, and also to show progress while I'm trying to figure out how to delete a currently running channel (the main point of this thread).

    Here is my latest code - please comment!

    -Craig

    UPDATE: Added listchannels()

    use strict; use warnings; use Net::SSH::Perl; my $HOST='MYHOST'; my $ID='MYLOGIN'; my $DEBUG=0; my $PW='MYPW'; my $perlcmd = join('', <DATA>); my $cmd = "/bin/perl - -heartbeat "; # Login to host... my $myssh = Net::SSH::Perl->new( $HOST, protocol=>2, debug=>$DEBUG ); $myssh->login($ID, $PW) or die "Failed to login: $!\n"; listchannels($myssh); # Alarm Handler... $SIG{ALRM} = sub { print STDERR "\nALARM\n"; listchannels($myssh); # Break out of currently running channel.. $myssh->break_client_loop; # Seems to work }; # Display output of multiple commands running on multiple SSH2 channel +s... while(1) { print STDERR "============================\n"; alarm 5; remcount($myssh); # Add new channel print STDERR "NEXT...\n"; } sub listchannels { my $ssh = shift || die "Missing ssh object"; foreach my $x (@{$ssh->{channel_mgr}{channels}}) { print STDERR "CHAN ", $x->{id}, " :$x\n"; } } sub remcount { my $ssh = shift || die "Missing ssh object"; # STDOUT handler (protocol 2 only)... $ssh->register_handler('stdout', sub { my ($channel, $buffer) = @_; print STDERR "CHAN $channel->{id}: ", $buffer->bytes, "\n"; }); print STDERR "REMOTE: $cmd\n"; my ($stdout, $stderr, $exit) = $ssh->cmd($cmd, $perlcmd); } __DATA__ use strict; use warnings; $|++; my $c='a'; while(1) { print $c++; sleep 1; }