Remote.pm revision 1.6
1#	$OpenBSD: Remote.pm,v 1.6 2015/06/25 19:29:57 bluhm Exp $
2
3# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
4#
5# Permission to use, copy, modify, and distribute this software for any
6# purpose with or without fee is hereby granted, provided that the above
7# copyright notice and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17use strict;
18use warnings;
19
20package Remote;
21use parent 'Proc';
22use Carp;
23use Cwd;
24use File::Basename;
25
26my %PIPES;
27
28sub close_pipes {
29	my @pipes = @_ ? @_ : keys %PIPES
30	    or return;
31	foreach (@pipes) {
32		# file descriptor cannot be a hash key, so use hash value
33		my $fh = $PIPES{$_};
34		# also print new line as close is delayed by forked processes
35		print $fh "close\n";
36		close($fh);
37	}
38	sleep 1;  # give other end a chance to finish process
39	delete @PIPES{@pipes};
40}
41
42END {
43	close_pipes();
44}
45
46sub new {
47	my $class = shift;
48	my %args = @_;
49	$args{logfile} ||= "remote.log";
50	$args{up} ||= "listen sock: ";
51	$args{down} ||= $args{dryrun} ? "relayd.conf" : "parent terminating";
52	$args{func} = sub { Carp::confess "$class func may not be called" };
53	$args{remotessh}
54	    or croak "$class remote ssh host not given";
55	$args{forward}
56	    or croak "$class forward not given";
57	my $self = Proc::new($class, %args);
58	$self->{listenaddr}
59	    or croak "$class listen addr not given";
60	$self->{connectaddr}
61	    or croak "$class connect addr not given";
62	$self->{connectport}
63	    or croak "$class connect port not given";
64	return $self;
65}
66
67sub run {
68	my $self = Proc::run(shift, @_);
69	$PIPES{$self->{pipe}} = $self->{pipe};
70	return $self;
71}
72
73sub up {
74	my $self = Proc::up(shift, @_);
75	my $lsock = $self->loggrep(qr/^listen sock: /)
76	    or croak ref($self), " no 'listen sock: ' in $self->{logfile}";
77	my($addr, $port) = $lsock =~ /: (\S+) (\S+)$/
78	    or croak ref($self), " no listen addr and port in $self->{logfile}";
79	$self->{listenaddr} = $addr;
80	$self->{listenport} = $port;
81	return $self;
82}
83
84sub child {
85	my $self = shift;
86
87	my @opts = split(' ', $ENV{SSH_OPTIONS}) if $ENV{SSH_OPTIONS};
88	my @sudo = $ENV{SUDO} ? "SUDO=$ENV{SUDO}" : ();
89	my @ktrace = $ENV{KTRACE} ? "KTRACE=$ENV{KTRACE}" : ();
90	my @relayd = $ENV{RELAYD} ? "RELAYD=$ENV{RELAYD}" : ();
91	my $dir = dirname($0);
92	$dir = getcwd() if ! $dir || $dir eq ".";
93	my @cmd = ("ssh", @opts, $self->{remotessh},
94	    @sudo, @ktrace, @relayd, "perl",
95	    "-I", $dir, "$dir/".basename($0), $self->{forward},
96	    $self->{listenaddr}, $self->{connectaddr}, $self->{connectport},
97	    ($self->{testfile} ? "$dir/".basename($self->{testfile}) : ()));
98	print STDERR "execute: @cmd\n";
99	exec @cmd;
100	die ref($self), " exec '@cmd' failed: $!";
101}
102
103sub close_child {
104	my $self = shift;
105	close_pipes(delete $self->{pipe});
106	return $self;
107}
108
1091;
110