--- /dev/null
+#!/usr/bin/perl
+
+=pod
+
+=head1 NAME $RCSfile: tunnel.pl,v $
+
+Set up a tunnel for emailing
+
+=head1 VERSION
+
+=over
+
+=item Author
+
+Andrew DeFaria <Andrew@ClearSCM.com>
+
+=item Revision:
+
+$Revision: 1.0 $
+
+=item Created:
+
+Wed 19 Aug 2020 09:09:09 AM MST
+
+=item Modified:
+
+$Date: $
+
+=back
+
+=head1 SYNOPSIS
+
+ Usage: tunnel.pl [-u|sage] [-h|elp] [-ve|rbose] [-d|ebug]
+
+ Where:
+
+ -u|sage: Displays this usage
+ -h|elp: Display full help
+ -ve|rbose: Be verbose
+ -d|ebug: Output debug messages
+ -host1: First host for tunnel (Default: localhost)
+ -port1: Port for host1
+ -host2: Second host for tunnel (Default: defaria.com)
+ -port2: Port for host2
+ -a|nnounce: Whether to announce startup (Default false)
+ -maxtretries: Maximum number of retry attempt to reestablish tunnel
+ (Default 3)
+ -nodaemon: Whether to go into daemon mode (Default: Daemon mode)
+
+=head1 DESCRIPTION
+
+This script sets up an SSH tunnel for the purposes of emailing.
+
+=cut
+
+use strict;
+use warnings;
+
+use File::Temp qw(tempfile);
+use FindBin;
+use Getopt::Long;
+use Net::OpenSSH;
+use POSIX ':sys_wait_h';
+
+use lib "$FindBin::Bin/../lib";
+
+use Pod::Usage;
+
+use Display;
+use Logger;
+use Utils;
+
+my $VERSION = '$Revision: 1.0 $';
+ ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);
+
+my %opts = (
+ usage => sub { pod2usage },
+ help => sub { pod2usage (-verbose => 2)},
+ verbose => sub { set_verbose },
+ debug => sub { set_debug },
+ host1 => 'localhost',
+ port1 => 1025,
+ host2 => 'defaria.com',
+ port2 => 25,
+ remotehost => 'defaria.com',
+ maxretries => 3,
+ daemon => 1,
+);
+
+my ($log, $ssh);
+
+sub Say($) {
+ my ($msg) = @_;
+
+ if (-f "$FindBin::Bin/shh") {
+ $log->msg("Not speaking because we were asked to be quiet - $msg");
+
+ return;
+ } # if
+
+ my ($status, @output) = Execute "/usr/local/bin/gt \"$msg\"";
+
+ $log->err("Unable to speak (Status: $status) - "
+ . join ("\n", @output), $status) if $status;
+
+ return;
+} # Say
+
+sub Report ($;$) {
+ my ($msg, $err) = @_;
+
+ Say $msg;
+
+ if ($err) {
+ $log->err($msg, $err);
+ } else {
+ $log->err($msg);
+ } # if
+
+ return;
+} # Report
+
+sub interrupt {
+ Report "Tunnel killed unexpectedly", 1;
+
+ kill 'INT', $ssh->get_master_pid;
+} # interrupt
+
+sub tunnel() {
+ my $tunnelStr = "-NL$opts{host1}:$opts{port1}:$opts{host2}:$opts{port2}";
+
+ my $retryattempts = 0;
+
+RETRY:
+ my ($fh, $filename) = tempfile;
+
+ my $ssh = Net::OpenSSH->new(
+ $opts{remotehost},
+ master_opts => $tunnelStr,
+ default_stderr_file => $filename
+ );
+
+ Report("Unable to establish ssh tunnel " . $ssh->error, 1) if $ssh->error;
+
+ my @lines = <$fh>;
+
+ close $fh;
+
+ unlink $filename;
+
+ if (grep /address already in use/i, @lines) {
+ Report 'Unable to start tunnel - Address already in use', 1;
+ } else {
+ my $msg = 'Ssh tunnel ';
+ $msg .= $retryattempts ? 'reestablished' : 'established';
+
+ Say $msg if $opts{announce};
+
+ $log->msg($msg);
+
+ # Wait for master to exit
+ waitpid($ssh->get_master_pid, WUNTRACED);
+
+ Report("Ssh tunnel terminated unexpectedly - Maximum retry count hit ($opts{maxretries}) - giving up", 1)
+ if $retryattempts++ >= $opts{maxretries};
+
+ $opts{announce} = $retryattempts;
+
+ Report 'Ssh tunnel terminated unexpectedly - Attempting restart';
+
+ goto RETRY;
+ } # if
+
+ return;
+} # tunnel
+
+## Main
+GetOptions (
+ \%opts,
+ 'usage',
+ 'help',
+ 'verbose',
+ 'debug',
+ 'host1',
+ 'host2',
+ 'port1',
+ 'port2',
+ 'announce!',
+ 'maxretries=i',
+ 'daemon!',
+) || Usage;
+
+# Turn off daemon mode if we are in the Perl debugger;
+no warnings; # Ignore warning about used only once $DB::OUT when not in debugger
+$opts{daemon} = 0 if defined $DB::OUT;
+use warnings;
+
+$log = Logger->new(
+ path => '/var/log',
+ name => "$Logger::me",
+ timestamped => 'yes',
+ append => 'yes',
+);
+
+$log->msg("$FindBin::Script v$VERSION");
+
+$SIG{INT} = $SIG{TERM} = \&interrupt;
+
+EnterDaemonMode if $opts{daemon};
+
+tunnel;
Message to display
-=item $handle:
+=item $handle:
File handle to display to (Default: STDERR)
-=item $nolinefeed:
+=item $nolinefeed:
If defined no linefeed is displayed at the end of the message.
return
unless $debug;
-
+
return
if $debug == 0;
-
+
$level ||= 1;
$msg ||= '';
if (($handle and -t $handle) or (-t *STDERR)) {
$msg = color ('cyan')
. $me
- . color ('reset')
- . ": "
- . color ('magenta')
- . "DEBUG"
- . color ('reset')
- . ": $msg";
+ . color ('reset')
+ . ': '
+ . color ('magenta')
+ . "DEBUG"
+ . color ('reset')
+ . ": $msg";
} else {
$msg = "$me: DEBUG: $msg";
} # if
display_err $msg, $handle, $nolinefeed if $debug and $level <= $debug;
-
+
return;
} # debug
my ($msg, $handle, $nolinefeed) = @_;
debug $msg, $handle, $nolinefeed, 1;
-
+
return;
} # debug1
sub debug2 ($;$$) {
my ($msg, $handle, $nolinefeed) = @_;
-
+
debug $msg, $handle, $nolinefeed, 2;
return;
sub debug3 ($;$$) {
my ($msg, $handle, $nolinefeed) = @_;
-
+
debug $msg, $handle, $nolinefeed, 2;
return;
Message to display
-=item $handle:
+=item $handle:
File handle to display to (Default: STDOUT)
-=item $nolinefeed:
+=item $nolinefeed:
If defined no linefeed is displayed at the end of the message.
print $handle $msg;
print $handle "\n" unless $nolinefeed;
-
+
return;
} # display
print $handle $msg;
print $handle "\n" if !$nolinefeed;
-
+
return;
} # display_err
sub display_error ($;$$$) {
my ($msg, $errno, $handle, $nolinefeed) = @_;
-
+
=pod
=head2 display_error ($msg, $errno, $handle, $nolinefeed)
=cut
$msg ||= '';
-
+
unless ($errno) {
if (($handle and -t $handle) or (-t *STDERR) and ($Config{perl} ne 'ratlperl')) {
$msg = color ('cyan')
. $me
. color ('reset')
- . ": "
- . color ('red')
- . "ERROR"
- . color ('reset')
- . ": $msg";
+ . ': '
+ . color ('red')
+ . 'ERROR'
+ . color ('reset')
+ . ": $msg";
} else {
$msg = "$me: ERROR: $msg";
} # if
} else {
if (($handle and -t $handle) or (-t *STDERR) and ($Config{perl} ne 'ratlperl')) {
$msg = color ('cyan')
- . $me
- . color ('reset')
- . ": "
- . color ('red')
- . "ERROR #$errno"
- . color ('reset')
- . ": $msg";
+ . $me
+ . color ('reset')
+ . ': '
+ . color ('red')
+ . "ERROR #$errno"
+ . color ('reset')
+ . ": $msg";
} else {
$msg = "$me: ERROR #$errno: $msg";
} # if
} # if
display_err $msg, $handle, $nolinefeed;
-
+
return;
} # display_error
Message to display
-=item $handle:
+=item $handle:
File handle to display to (Default: STDOUT)
=cut
display $msg, $handle, "nolf";
-
+
return;
} # display_nolf
Message to display
-=item $handle:
+=item $handle:
File handle to display to (Default: STDOUT)
display_error $msg, $errno, $handle, $nolinefeed;
exit $errno if $errno;
-
+
return;
} # error
sub set_me {
my ($whoami) = @_;
-
+
=pod
=head2 set_me ($me)
=cut
$me = $whoami;
-
+
return;
} # set_me
return
unless $trace;
-
+
$msg = $msg ? ": $msg" : '';
$type ||= 'In';
if (-t STDOUT) {
display color ('cyan')
. "$type "
- . color ('yellow')
- . color ('bold')
- . $subroutine
- . color ('reset')
- . $msg;
+ . color ('yellow')
+ . color ('bold')
+ . $subroutine
+ . color ('reset')
+ . $msg;
} else {
display "$type $subroutine$msg";
- } # if
+ } # if
return $subroutine;
} # trace
sub trace_enter (;$) {
my ($msg) = @_;
-
+
=pod
=head2 trace_enter
sub trace_exit (;$) {
my ($msg) = @_;
-
+
=pod
=head2 trace_exit
=cut
trace $msg, "EXIT";
-
+
return
} # trace_exit
$level ||= 1;
$verbose ||= 0;
-
+
display $msg, $handle, $nolinefeed if $verbose and $level <= $verbose;
-
+
return;
} # verbose
sub verbose1 ($;$$) {
my ($msg, $handle, $nolinefeed) = @_;
-
+
verbose $msg, $$handle, $nolinefeed, 1;
-
+
return;
} # verbose1
sub verbose2 ($;$$) {
my ($msg, $handle, $nolinefeed) = @_;
-
+
verbose $msg, $handle, $nolinefeed, 2;
-
+
return;
} # verbose1
sub verbose3 ($;$$) {
my ($msg, $handle, $nolinefeed) = @_;
-
+
verbose $msg, $handle, $nolinefeed, 3;
-
+
return;
} # verbose1
=cut
verbose $msg, $handle, "nolf";
-
+
return;
} # verbose_nolf
Message to display
-=item $handle:
+=item $handle:
File handle to display to (Default: STDOUT)
-=item $nolinefeed:
+=item $nolinefeed:
If defined no linefeed is displayed at the end of the message.
unless ($warnno) {
if (($handle and -t $handle) or (-t *STDERR) and ($Config{perl} ne 'ratlperl')) {
$msg = color ('cyan')
- . $me
- . color ('reset')
- . ": "
- . color ('yellow')
- . "WARNING"
- . color ('reset')
- . ": $msg";
+ . $me
+ . color ('reset')
+ . ": "
+ . color ('yellow')
+ . "WARNING"
+ . color ('reset')
+ . ": $msg";
} else {
$msg = "$me: WARNING: $msg";
} # if
} else {
if (($handle and -t $handle) or (-t *STDERR) and ($Config{perl} ne 'ratlperl')) {
$msg = color ('cyan')
- . $me
- . color ('reset')
- . ": "
- . color ('yellow')
- . "WARNING #$warnno"
- . color ('reset')
- . ": $msg";
+ . $me
+ . color ('reset')
+ . ": "
+ . color ('yellow')
+ . "WARNING #$warnno"
+ . color ('reset')
+ . ": $msg";
} else {
$msg = "$me: WARNING #$warnno: $msg";
} # if
} # if
display_err $msg, $handle, $nolinefeed;
-
+
return;
} # warning