£okì has asked for the wisdom of the Perl Monks concerning the following question:

I have a simple Net::SSH2 script that logs in and runs a command. However, the command I have to run is very similar to 'shutdown -r now' in that it kills the ssh connection. Using Net::SSH2's channels I do this:
$ssh->scp_put($firmware[$antenna_type],'/tmp/fwupdate.bin'); print "Upgrading firmware, this will take about 3 minutes.\n"; my $chan = $ssh->channel(); $chan->exec("/sbin/fwupdate -m\n"); $chan->close;
If I run ANY other command that does not cause a session timeout, it works just fine. However, if I do this, it just hangs. Is there any way people can think of putting a timeout on this or just have it just go off to neverneverland? I tried using threads::shared but I couldn't seem to pass the reference to $ssh on to my thread.
Siggy!.

Replies are listed 'Best First'.
Re: Net::SSH2 exec timeout
by BrowserUk (Patriarch) on Sep 08, 2011 at 04:02 UTC
    I tried using threads::shared but I couldn't seem to pass the reference to $ssh on to my thread.

    Try it this way:

    use threads; use Net::... my $ssh = ...; $ssh->scp_put($firmware[$antenna_type],'/tmp/fwupdate.bin'); async { print "Upgrading firmware, this will take about 3 minutes.\n"; my $chan = $ssh->channel(); $chan->exec("/sbin/fwupdate -m\n"); $chan->close; }->detach; ...

    That should "work" and allow your main thread to continue. The thread Will hang around until the connection times out, but is that a problem?

    Alternatively, could you detach the remote process into the background using &:

    $ssh->scp_put($firmware[$antenna_type],'/tmp/fwupdate.bin'); print "Upgrading firmware, this will take about 3 minutes.\n"; my $chan = $ssh->channel(); $chan->exec( "/sbin/fwupdate -m &\n"); ### Note: & $chan->close;

    Wouldn't that allow the connection to complete in a timely fashion?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      I like your solution and am perfectly fine with it hanging until the timeout. . However, I still get the same error as I would with a normal threads in that $ssh is not correctly passed
      Thread 1 terminated abnormally: Can't call method "exec" on an undefined value at Configuration.pl
      If I pass the $ssh in as a ref:
      $ssh_ref = \$ssh; async { print "Upgrading firmware, this will take about 3 minutes. +\n"; my $chan = $$ssh_ref->channel(); $chan->exec("/sbin/fwupdate -m\n"); $chan->close; }->detach;

      I get this error. . I have NO idea what it means.
      perl: ath.c:193: _gcry_ath_mutex_lock: Assertion `*lock == ((ath_mutex_t) 0)' failed.
      Maybe I need to lock the var before I use it? Thoughts?

      The & does not seem to help either. .

        Why are you using a reference to the object instead of the object?


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Net::SSH2 exec timeout
by salva (Canon) on Sep 08, 2011 at 07:31 UTC
    the fwupdate command may reset the remote machine without doing a proper shutdown, leaving TCP connections unclosed.

    Instead of closing the channel, try closing the socket and then disconnecting the full session:

    my $socket = $ssh->socket; my $chan = $ssh->channel(); $chan->exec("/sbin/fwupdate -m\n"); close $socket; $ssh->disconnect;

    Though, even then there is a race condition: fwupdate may be faster taking control of the machine than the SSH server responding to the SSH_MSG_CHANNEL_REQUEST request.

    Setting blocking to 0 could help there, but them, the exec method may fail and you will never know if the command was delivered or not:

    my $chan = $ssh->channel(); $ssh->blocking(0); $chan->exec("/sbin/fwupdate -m\n");

    Alternatively, if you are running your script in a Unix/Linux box, instead of Net::SSH2, you could use Net::OpenSSH (the OpenSSH client used by the module under the hood is able to detect staled connections):

    use Net::OpenSSH; my $ssh = Net::OpenSSH->new($host, timeout => 30); $ssh->scp_put($firmware[$antenna_type],'/tmp/fwupdate.bin'); $ssh->system("/sbin/fwupdate -m\n");
      I tried the disconnect. Unfortunately the code does not continue past the channel exec. If you throw in the $ssh->disconnect, it never even gets there.

      blocking(0) seems to have no effect. I had actually tried that, forgot to mention it.

      OpenSSH: Yeah, thought of that as well but the code has to be usable from multiple different systems.
        would your busydos shell accept the following command?
        (sleep 5 && /sbin/fwupdate -m) >/dev/null 2>&1 </dev/null &
        That would execute the command after 5 seconds, time enough for the SSH session to be closed.
Re: Net::SSH2 exec timeout
by zentara (Cardinal) on Sep 08, 2011 at 10:38 UTC
      Don't I WISH!! Unfortunately the OS I'm connecting to does not contain nohup. It's a busybox linux on an embedded device.
        Unfortunately the OS I'm connecting to does not contain nohup. It's a busybox linux on an embedded device.

        You can implement nohup as a shell script. See src/nohup.sh in ftp://ftp.slackware.at/slackware-3.3/source/a/sh_utils/sh-utils-1.16.tar.gz and the resulting usr/bin/nohup in ftp://ftp.slackware.at/slackware-3.3/slakware/a8/sh_utils.tgz. Reduced to the bare minimum, you just need a shell that implements the trap and exec commands. Having nice is a nice extra, but it is not strictly required; and even cat and [ (test) are not needed if you make sure you always redirect STDOUT and STDERR when invoking the nohup script.

        Alexander

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