Added USR1 handling
[clearscm.git] / bin / tunnel.pl
1 #!/usr/bin/perl
2
3 =pod
4
5 =head1 NAME $RCSfile: tunnel.pl,v $
6
7 Set up a tunnel for emailing
8
9 =head1 VERSION
10
11 =over
12
13 =item Author
14
15 Andrew DeFaria <Andrew@ClearSCM.com>
16
17 =item Revision:
18
19 $Revision: 1.0 $
20
21 =item Created:
22
23 Wed 19 Aug 2020 09:09:09 AM MST
24
25 =item Modified:
26
27 $Date: $
28
29 =back
30
31 =head1 SYNOPSIS
32
33  Usage: tunnel.pl [-u|sage] [-h|elp] [-ve|rbose] [-d|ebug]
34  
35  Where:
36
37  -u|sage:      Displays this usage
38  -h|elp:       Display full help
39  -ve|rbose:    Be verbose
40  -d|ebug:      Output debug messages
41  -host1:       First host for tunnel (Default: localhost)
42  -port1:       Port for host1
43  -host2:       Second host for tunnel (Default: defaria.com)
44  -port2:       Port for host2
45  -a|nnounce:   Whether to announce startup (Default false)
46  -maxtretries: Maximum number of retry attempt to reestablish tunnel
47                (Default 3)
48  -nodaemon:    Whether to go into daemon mode (Default: Daemon mode)
49
50 =head1 DESCRIPTION
51
52 This script sets up an SSH tunnel for the purposes of emailing.
53
54 =cut
55
56 use strict;
57 use warnings;
58
59 use File::Temp qw(tempfile);
60 use FindBin;
61 use Getopt::Long;
62 use Net::OpenSSH;
63 use POSIX ':sys_wait_h';
64
65 use lib "$FindBin::Bin/../lib";
66
67 use Pod::Usage;
68
69 use Display;
70 use Logger;
71 use Utils;
72
73 my $VERSION  = '$Revision: 1.0 $';
74   ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);
75
76 my %opts = (
77   usage      => sub { pod2usage },
78   help       => sub { pod2usage (-verbose => 2)},
79   verbose    => sub { set_verbose },
80   debug      => sub { set_debug },
81   host1      => 'localhost',
82   port1      => 1025,
83   host2      => 'defaria.com',
84   port2      => 25,
85   remotehost => 'defaria.com',
86   maxretries => 3,
87   daemon     => 1,
88 );
89
90 my ($log, $ssh);
91
92 sub Say($) {
93   my ($msg) = @_;
94
95   if (-f "$FindBin::Bin/shh") {
96     $log->msg("Not speaking because we were asked to be quiet - $msg");
97
98     return;
99   } # if
100
101   my ($status, @output) = Execute "/usr/local/bin/gt \"$msg\"";
102
103   $log->err("Unable to speak (Status: $status) - "
104           . join ("\n", @output), $status) if $status;
105
106   return;
107 } # Say
108
109 sub Report ($;$) {
110   my ($msg, $err) = @_;
111
112   Say $msg;
113
114   if ($err) {
115     $log->err($msg, $err);
116   } else {
117     $log->err($msg);
118   } # if
119
120   return;
121 } # Report
122
123 sub interrupt {
124    Report "Tunnel killed unexpectedly", 1;
125
126    kill 'INT', $ssh->get_master_pid;
127
128    return;
129 } # interrupt
130
131 sub tunnel() {
132   my $tunnelStr = "-NL$opts{host1}:$opts{port1}:$opts{host2}:$opts{port2}";
133
134   my $retryattempts = 0;
135
136 RETRY:
137   my ($fh, $filename) = tempfile;
138
139   my $ssh = Net::OpenSSH->new(
140     $opts{remotehost},
141     master_opts         => $tunnelStr,
142     default_stderr_file => $filename
143   );
144
145   Report("Unable to establish ssh tunnel " . $ssh->error, 1) if $ssh->error;
146
147   # Check to see if address is already in use
148   my @lines = <$fh>;
149
150   close $fh;
151
152   unlink $filename;
153
154   if (grep /address already in use/i, @lines) {
155     Report 'Unable to start tunnel - Address already in use', 1;
156   } else {
157     my $msg  = 'Ssh tunnel ';
158        $msg .= $retryattempts ? 'reestablished' : 'established';
159
160     Say $msg if $opts{announce};
161
162     $log->msg($msg);
163
164     # Reset retry attempts since we reestablished the tunnel
165     $retryattempts = 0 if $retryattempts;
166
167     # Wait for master to exit
168     waitpid($ssh->get_master_pid, WUNTRACED);
169
170     Report("Ssh tunnel terminated unexpectedly - Maximum retry count hit ($opts{maxretries}) - giving up", 1)
171       if $retryattempts++ >= $opts{maxretries};
172
173     $opts{announce} = $retryattempts;
174
175     Report 'Ssh tunnel terminated unexpectedly - Attempting restart';
176
177     undef $ssh;
178
179     goto RETRY;
180   } # if
181
182   return;
183 } # tunnel
184
185 ## Main
186 GetOptions (
187   \%opts,
188   'usage',
189   'help',
190   'verbose',
191   'debug',
192   'host1',
193   'host2',
194   'port1',
195   'port2',
196   'announce!',
197   'maxretries=i',
198   'daemon!',
199 ) || Usage;
200
201 # Turn off daemon mode if we are in the Perl debugger;
202 no warnings; # Ignore warning about used only once $DB::OUT when not in debugger
203 $opts{daemon} = 0 if defined $DB::OUT;
204 use warnings;
205
206 $log = Logger->new(
207   path        => '/var/log',
208   name        => "$Logger::me",
209   timestamped => 'yes',
210   append      => 'yes',
211 );
212
213 $log->msg("$FindBin::Script v$VERSION");
214
215 $SIG{INT} = $SIG{TERM} = \&interrupt;
216
217 EnterDaemonMode if $opts{daemon};
218
219 tunnel;