Remote.pm revision 1.3
1#	$OpenBSD: Remote.pm,v 1.3 2014/06/22 14:18:01 bluhm Exp $
2
3# Copyright (c) 2010-2013 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} ||= "Started";
51	$args{down} ||= $args{dryrun} ? "no actions" : "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 $timeout = shift || 10;
76	my $lsock = $self->loggrep(qr/^listen sock: /, $timeout)
77	    or croak ref($self), " no listen sock in $self->{logfile} ".
78		"after $timeout seconds";
79	my($addr, $port) = $lsock =~ /: (\S+) (\S+)$/
80	    or croak ref($self), " no listen addr and port in $self->{logfile}";
81	$self->{listenaddr} = $addr;
82	$self->{listenport} = $port;
83	return $self;
84}
85
86sub child {
87	my $self = shift;
88
89	print STDERR $self->{up}, "\n";
90	my @opts = split(' ', $ENV{SSH_OPTIONS}) if $ENV{SSH_OPTIONS};
91	my @sudo = $ENV{SUDO} ? "SUDO=$ENV{SUDO}" : ();
92	my @ktrace = $ENV{KTRACE} ? "KTRACE=$ENV{KTRACE}" : ();
93	my @relayd = $ENV{RELAYD} ? "RELAYD=$ENV{RELAYD}" : ();
94	my $curdir = dirname($0) || ".";
95	$curdir = getcwd() if $curdir eq '.';
96	my @cmd = ('ssh', @opts, $self->{remotessh},
97	    @sudo, @ktrace, @relayd, 'perl',
98	    '-I', $curdir, "$curdir/".basename($0), $self->{forward},
99	    $self->{listenaddr}, $self->{connectaddr}, $self->{connectport},
100	    ($self->{testfile} ? "$curdir/".basename($self->{testfile}) : ()));
101	print STDERR "execute: @cmd\n";
102	exec @cmd;
103	die "Exec @cmd failed: $!";
104}
105
106sub close_child {
107	my $self = shift;
108	close_pipes(delete $self->{pipe});
109	return $self;
110}
111
1121;
113