r4324 added RHEL6
[racktables] / gateways / deviceconfig / nc.pl
1 #!/usr/bin/perl -w
2 use strict;
3 use Getopt::Long qw(:config pass_through);;
4 use Fcntl;
5 use IPC::Open2;
6 my $NC_EXEC = 'nc';
7
8 my ($marker, $marker_halt);
9 my $wait_for;
10 my $help;
11 GetOptions ("stopwhen=s" => \$marker,
12 "haltwhen=s" => \$marker_halt,
13 "putwhen=s" => \$wait_for,
14 "help|h" => \$help);
15 if ($help) {
16 print <<END;
17 Wrapper around nc for non-interactive sessions.
18 It can close session when it sees the regexp-matching string in session output.
19 It can wait for command prompt to push the next command to remote side.
20 It DOES NOT directly link its standard input with nc`s standard input.
21
22 Additional options availivle:
23 --help, -h This help message
24 --stopwhen=<regexp> Close nc when this string is seen in the output
25 --putwhen=<regexp> Put next line only if last output line complies to regexp
26
27 Original nc options:
28 END
29 exec($NC_EXEC);
30 exit;
31 }
32
33 my $hostname = 'unknown';
34 if (@ARGV >= 2) {
35 $hostname = $ARGV[$#ARGV - 1];
36 }
37 my ($break_re, $prompt_re, $halt_re);
38 if ($marker) {
39 $break_re = qr/($marker)/m;
40 }
41 if ($marker_halt) {
42 $halt_re = qr/($marker_halt)/m;
43 }
44 if ($wait_for) {
45 $prompt_re = qr/$wait_for/;
46 }
47 $| = 1;
48
49 my @cmds = <STDIN>;
50
51 my $log = '';
52 local (*Reader, *Writer);
53 my $pid = open2(\*Reader, \*Writer, $NC_EXEC, @ARGV) or die "cant start $NC_EXEC: $!\n";
54 my $stdin_ended = 0;
55
56 my $total_read = 0;
57 my $vec = '';
58 while () {
59 vec($vec, fileno(Reader), 1) = 1;
60 select($vec, undef, undef, undef);
61
62 my $buff;
63 my $nb = sysread(Reader, $buff, 64 * 1024);
64 $total_read += $nb;
65 print $buff if $nb; # echo to STDOUT
66 if (! $nb) {
67 if (@cmds and $total_read) {
68 print STDERR "$hostname: connection interrupted by remote side.\n";
69 }
70 last; # exit if nc closed connection
71 }
72
73 $log .= $buff;
74 my $halt_matched = $halt_re && $log =~ $halt_re;
75 if ($halt_matched or $break_re && $log =~ $break_re) {
76 if ($halt_matched) {
77 chomp $log;
78 $log =~ s/.*\n//;
79 $log =~ s/[^ -~]//g;
80 print STDERR "$hostname: Matched line '$log', connection interrupted.\n";
81 exit 1;
82 }
83 else {
84 exit 0;
85 }
86 }
87 $log =~ s/.*\n//s; # keep only last line in log
88
89 if ($prompt_re and $log =~ $prompt_re) { # if interative mode and pending commands
90 if (@cmds) {
91 print Writer shift @cmds; # push one command
92 $log = '';
93 }
94 else { # if interactive mode and no more commands, exit
95 last;
96 }
97 }
98 elsif (! $prompt_re and @cmds) { # if non-interactive mode and pending commands, push them all
99 print Writer @cmds;
100 undef @cmds;
101 }
102 }
103 close Reader;
104 close Writer;
105 waitpid( $pid, 0 );
106 exit $? >> 8;