Fellow monks, for many months have I struggled with a single goal: to simultaneously control multiple ssh sessions from a perl script. As I discovered, using Expect.pm in series becomes painfully with many machines, and Perl Threading does not seem to be compatible with IO-Tty.

However, at long last an answer is to be had. A code skeleton follows (for demonstration purposes only):
#!/usr/local/perl-5.8.0/bin/perl use strict; use threads; use Expect; ####### # # Here is the section that we want to run many times in parallel # ####### sub mysub { my $remoteshell; unless ( $remoteshell = Expect -> spawn ("ssh2 192.168.1.1") ) { die "error spawning"; } $remoteshell -> log_stdout(1); unless ( $remoteshell -> expect (120, [ "ssword:" => sub { $remoteshell -> send ("mypasswo +rd\n"); } + ], ) ) { die "no password prompt"; } unless ( $remoteshell -> expect (20,"ro\@charon:") ) { die "no prompt"; } print $remoteshell "touch /tmp/hopo$$\n"; $remoteshell ->soft_close(); } ###### # # Here is the replacement for: # my $h = threads->new(\&mysub); # $h->join; # ###### my $PARENTPROCESS=$$; my @streamlist; my @childlist; for (every machine); my $KID; my $child = open($KID,"-|"); if ($$==$PARENTPROCESS) { push @streamlist, $KID; push @childlist, $child; next; } else { $| = 1; printf("begin--running forked ssh session on $thismachine--\n"); mysub; printf("end--running forked ssh session on $thismachine--\n"); exit(); } } #let child processes terminate my $kid; do { $kid = wait(); } until $kid == -1; #print all the output foreach my $stream (@streamlist) { while (<$stream>) { print "\t$_"; } }

I appreciate any comments / criticisms on this code. I will likely be using it in production tomorrow. I made a module that I will be using (available on request) to update a bunch of systems. All passwords are stored in encrypted file which are decrypted at runtime.

Rohit

Replies are listed 'Best First'.
Re: fork + Expect.pm + ssh == success!
by Juerd (Abbot) on Dec 26, 2002 at 11:40 UTC

    Is there any specific reason for not using Net::SSH::Perl here?

    use Net::SSH::Perl; my $ssh = Net::SSH::Perl->new($host); $ssh->login($user, $pass); my($stdout, $stderr, $exit) = $ssh->cmd($cmd);

    Note you're never calling mysub(), you're only referencing it in void context. Use mysub; instead of \&mysub;.

    You seem to want to replace threads with forks. Why not use forks then? It's a drop-in replacement for threads. (I expect Net::SSH::Perl to work with threads. Haven't tried, though.)

    Note that if *you* can decrypt a password, someone else can too! Consider using keys instead of passwords.

    - Yes, I reinvent wheels.
    - Spam: Visit eurotraQ.
    

      Hi, thanks for this information. Net::SSH is something I did not know about. It looks very interesting. However, (from glancing briefly over the documentation), it does not look like it supports passwords or passphrase encrypted private-keys. We require public key and password authentication.

      For someone else to decrypt the file containing my passwords (and passphrases), they would have to sniff my keyboard as I type in the decryption salt every time I run.

      forks (E/EL/ELIZABETH/forks-0.01.tar.gz) looks extremely cool, and I am going to play with this very soon :) I guess it pays to search CPAN first! Thanks for your help!

      Rohit

        Net::SSH is something I did not know about. It looks very interesting. However, (from glancing briefly over the documentation), it does not look like it supports passwords or passphrase encrypted private-keys.

        Net::SSH supports passwords from the tty and keys. It's just a mere wrapper for ssh itself. Net::SSH::Perl, on the other hand, does not just interface with ssh, but implements the ssh protocol in Perl. It supports passing passwords, and I think keys too.

        - Yes, I reinvent wheels.
        - Spam: Visit eurotraQ.