Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

threads and Net::OpenSSH

by rastoboy (Monk)
on Mar 15, 2012 at 15:50 UTC ( [id://959804]=perlquestion: print w/replies, xml ) Need Help??

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

Hi there friends,

So I'm writing my first perl program using threads, and it's actually doing what I want it to do, but I don't understand why :-)

Basically it's a testing program that wget's a file off a web server while tailing various logs:

#!/usr/bin/perl use strict; use warnings; use threads; use Net::OpenSSH; use Data::Dumper; my $sensor_ssh = Net::OpenSSH->new( 'rasto:password@10.3.12.175', 'master_opts' => [ '-o' => 'StrictHostKeyChecking=no' ] ); $sensor_ssh->error and die "Couldn't establish SSH connection: " . $sensor_ssh->error; my $sudo = $sensor_ssh->capture( { stderr_to_stdout => 1 }, 'echo \'password\' | sudo -S bash' ); print "initialize sudo: " . $sudo; my $wget_thread = threads->create( \&wget_sub ); my $sensor_syslog_thread = threads->create( \&tail_sensor_syslog, $sen +sor_ssh ); $wget_thread->join(); my $tail_syslog = $sensor_syslog_thread->join(); print "tail syslog: $tail_syslog\n"; my $test = $sensor_ssh->capture('ls') or warn "Failed: $sensor_ssh->er +ror \n"; print Dumper $sensor_ssh; sub wget_sub { my $wget = `wget http://10.3.13.4/xaa`; print "wget:\n$wget\n"; return 1; } sub tail_sensor_syslog { my $ssh = shift; print("In the thread\n"); my $cap = $ssh->capture( { stderr_to_stdout => 1 }, 'sudo tail -f /var/log/syslog' ); return $cap; }
So basically what happens is when the wget is finished, the tail -f going on stops. This is convenient for me at the moment, but the more I think about it I don't understand why.

One thing that is interesting is that if I attempt to use the $sensor_ssh connection again, it will warn in this code:

Failed: Net::OpenSSH=HASH(0xa21b220)->error
In the dump of the Net::OpenSSH object I see this bit:
'_error' => 'master ssh connection broken',
So I guess my question is, how is the master ssh connection being broken? And is there a better way to do this? It's convenient for me right now (as the join() does return the data) but I'm not to comfortable not understanding what's going on here, as I'm sure in the future it will come back and bite me :-)

Any input would be greatly appreciated!ls

Replies are listed 'Best First'.
Re: threads and Net::OpenSSH
by zentara (Archbishop) on Mar 15, 2012 at 16:23 UTC
      Thanks for that, Zentara, good information.
Re: threads and Net::OpenSSH
by BrowserUk (Patriarch) on Mar 15, 2012 at 18:15 UTC

    It's not clear to me if what you aren't understanding is related to the threading, or just to the operations of the ssh connections (of which I know nuu-thiiing; Nuu-thiiingg:)?

    As best as I can tell, your use of threading seems perfectly vanilla and sensible.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    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.

    The start of some sanity?

      BrowserUK: lol neither am I :-)

      I guess the main thing I don't understand is why my "tail -f" sessions die when I join those threads. My understanding of join() is that it tells the program to wait for the thread to finish. But a tail -f by itself would never finish, right?

        A quick look at the POD suggests that Net::OpenSSH implements timeouts that will close connections after a lack of network activity.

        A grep inside turns up this calculation which suggests that the default timeout is 90 (or 30?) seconds:

        my $timeout = int((($self->{_timeout} || 90) + 2)/3);

        My guess is that if the remote system is idle, tail -f syslog will produce no output and so the module closes the connection. Maybe you should consider using an explicit timeout.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        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.

        The start of some sanity?

        Hmm, actually I'm seeing in some cases that it merely detaches those tail -f threads. What would be the best way to actually terminate those tail -f threads when I'm ready? There seem to be a lot of options.
Re: threads and Net::OpenSSH
by salva (Canon) on Mar 16, 2012 at 16:01 UTC
    The first thing you should do is to ensure that you have installed and are using the latest stable version of the module, 0.57 as it incorporates some bug fixes affecting threads.

    If you are already using 0.57, enable debugging and post here the output:

    $Net::OpenSSH::debug = -1;
      Apologies for the delay in responding. I was indeed at version 0.50, and I was about to upgrade when I realized that I was actually dependent on this behavior to get my job done :-) So I finished my work before upgrading.

      After upgrading, the join() on the tail process hangs forever, as one might expect.

      Problem solved (sorta!). In any case now I can work on elegant ways to kill the threads, and anyways feel like what's going on makes sense.

      Thanks!

        In any case now I can work on elegant ways to kill the threads ...

        There is no "elegant way to kill a thread".

        Your best solution, assuming it is possible, would be to enable a timeout on the ssh connection.

        Your second best solution would be to use a timer/signal to interrupt the read. That is difficult (if not impossible), due to Perl SAFE SIGNALS.

        The third best solution would be to set the socket non-blocking and only read when there is something available. (Ie.Poll!)

        The problem with killing the thread -- assuming you discover a reliable mechanism for doing so(*) -- is that all the data you've already so carefully arranged to capture, would be discarded. Ie. As you are returning the captured data as the return value from the thread, you'll never see it if you kill the thread.

        (*)The problem here is that to kill the thread, you first need to interrupt the read. And if you can interrupt the read, there is no need to kill the thread.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        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.

        The start of some sanity?

        In any case now I can work on elegant ways to kill the threads

        This is elegant enough for me, but I'm easy. :-)

        #!/usr/bin/perl -w use strict; use threads; use threads::shared; my $timer_go:shared = 0; my $worker = threads->create(\&worker); my $timer = threads->create(\&timer,$worker)->detach(); print "hit enter to start\n"; <>; $timer_go=1; while( (scalar threads->list) > 0 ){ print scalar threads->list,"\n"; sleep 1; foreach my $thread (threads->list) { if( $thread->is_joinable ){ $thread->join;} } } print "worker joined, all done\n"; exit; sub timer { my $worker = shift; while(1){ if($timer_go){ my $count = 0; while(1){ $count++; if($count > 5){ print "timed out\n"; # Send a signal to a thread $worker->kill('INT'); return; # will destroy a detached thread } sleep 1; print "timing $count\n"; } }else{sleep 1} } } sub worker { $|++; $SIG{INT} = sub{ warn "Caught Zap!\n"; threads->exit() }; # threads->exit() will exit thread only while(1){sleep 1; next} return; }

        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://959804]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (5)
As of 2024-03-28 13:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found