summaryrefslogtreecommitdiff
path: root/FS/FS/part_export/shellcommands_expect.pm
diff options
context:
space:
mode:
Diffstat (limited to 'FS/FS/part_export/shellcommands_expect.pm')
-rw-r--r--FS/FS/part_export/shellcommands_expect.pm128
1 files changed, 128 insertions, 0 deletions
diff --git a/FS/FS/part_export/shellcommands_expect.pm b/FS/FS/part_export/shellcommands_expect.pm
new file mode 100644
index 000000000..c2a4118e2
--- /dev/null
+++ b/FS/FS/part_export/shellcommands_expect.pm
@@ -0,0 +1,128 @@
+package FS::part_export::shellcommands_expect;
+use base qw( FS::part_export::shellcommands );
+
+use strict;
+use Tie::IxHash;
+use Net::OpenSSH;
+use Expect;
+#use FS::Record qw( qsearch qsearchs );
+
+tie my %options, 'Tie::IxHash',
+ 'user' => { label =>'Remote username', default=>'root' },
+ 'useradd' => { label => 'Insert commands', type => 'textarea', },
+ 'userdel' => { label => 'Delete commands', type => 'textarea', },
+ 'usermod' => { label => 'Modify commands', type => 'textarea', },
+ 'suspend' => { label => 'Suspend commands', type => 'textarea', },
+ 'unsuspend' => { label => 'Unsuspend commands', type => 'textarea', },
+ 'debug' => { label => 'Enable debugging',
+ type => 'checkbox',
+ value => 1,
+ },
+;
+
+our %info = (
+ 'svc' => 'svc_acct',
+ 'desc' => 'Real time export via remote SSH, with interactive ("Expect"-like) scripting, for svc_acct services',
+ 'options' => \%options,
+ 'notes' => q[
+Interactively run commands via SSH in a remote terminal, like "Expect". In
+most cases, you probably want a regular shellcommands (or broadband_shellcommands, etc.) export instead, unless
+you have a specific need to interact with a terminal-based interface in an
+"Expect"-like fashion.
+<BR><BR>
+
+Each line specifies a string to match and a command to
+run after that string is found, separated by the first space. For example, to
+run "exit" after a prompt ending in "#" is sent, "# exit". You will need to
+<a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.9:Documentation:Administration:SSH_Keys">setup SSH for unattended operation</a>.
+<BR><BR>
+
+In commands, all variable substitutions of the regular shellcommands (or
+broadband_shellcommands, etc.) export are available (use a backslash to escape
+a literal $).
+]
+);
+
+sub _export_command {
+ my ( $self, $action, $svc_acct) = (shift, shift, shift);
+ my @lines = split("\n", $self->option($action) );
+
+ return '' unless @lines;
+
+ my @commands = ();
+ foreach my $line (@lines) {
+ my($match, $command) = split(' ', $line, 2);
+ my( $command_string ) = $self->_export_subvars( $svc_acct, $command, '' );
+ push @commands, [ $match, $command_string ];
+ }
+
+ $self->shellcommands_expect_queue( $svc_acct->svcnum, @commands );
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ my @lines = split("\n", $self->option('replace') );
+
+ return '' unless @lines;
+
+ my @commands = ();
+ foreach my $line (@lines) {
+ my($match, $command) = split(' ', $line, 2);
+ my( $command_string ) = $self->_export_subvars_replace( $new, $old, $command, '' );
+ push @commands, [ $match, $command_string ];
+ }
+
+ $self->shellcommands_expect_queue( $new->svcnum, @commands );
+}
+
+sub shellcommands_expect_queue {
+ my( $self, $svcnum, @commands ) = @_;
+
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::shellcommands_expect::ssh_expect",
+ };
+ $queue->insert(
+ user => $self->option('user') || 'root',
+ host => $self->machine,
+ debug => $self->option('debug'),
+ commands => \@commands,
+ );
+}
+
+sub ssh_expect { #subroutine, not method
+ my $opt = { @_ };
+
+ my $dest = $opt->{'user'}.'@'.$opt->{'host'};
+
+ open my $def_in, '<', '/dev/null' or die "unable to open /dev/null\n";
+ my $ssh = Net::OpenSSH->new( $dest, 'default_stdin_fh' => $def_in );
+ # ignore_all_errors doesn't override SSH connection/auth errors--
+ # probably correct
+ die "Couldn't establish SSH connection to $dest: ". $ssh->error
+ if $ssh->error;
+
+ my ($pty, $pid) = $ssh->open2pty
+ or die "Couldn't start a remote terminal session";
+ my $expect = Expect->init($pty);
+ #not useful #$expect->debug($opt->{debug} ? 3 : 0);
+
+ foreach my $line ( @{ $opt->{commands} } ) {
+ my( $match, $command ) = @$line;
+
+ warn "Waiting for '$match'\n" if $opt->{debug};
+
+ my $matched = $expect->expect(30, $match);
+ unless ( $matched ) {
+ my $err = "Never saw '$match'\n";
+ warn $err;
+ die $err;
+ }
+ warn "Running '$command'\n" if $opt->{debug};
+ $expect->send("$command\n");
+ }
+
+ '';
+}
+
+1;