#!/usr/bin/perl -w

# This example needs some work before it can support threads.
use strict;
use Net::SSH 'sshopen2';
use IPC::Open2;
use Filesys::Fuse3;
use Data::Dumper;

my $port; if($ARGV[-1]=~/^--/){ $port = pop(@ARGV); $port =~ s/--port=//; }
my ($host, $dir, $mount) = @ARGV;
if(!defined($mount)) {
	$mount = $dir;
	if($host =~ /^(.*):(.*)$/) {
		($host,$dir) = ($1,$2);
	} else {
		die "usage: $0 user\@host remotedir mountpoint [--port=<port>]\n".
		    "or   : $0 user\@host:remotedir mountpoint [--port=<port>]\n";
	}
}

`umount $mount` unless -d $mount;
die "mountpoint $mount isn't a directory!\n" unless -d $mount;

my (%args) = (mountpoint => $mount);

map { my ($str) = $_; $args{$str} = sub { netlink($str,@_) } }
	qw(getattr getdir open read write readlink unlink rmdir
	   symlink rename link chown chmod truncate utime mkdir
	   rmdir mknod statfs);

sub connect_remote {
	push(@Net::SSH::ssh_options, "-p $port") if $port;
	sshopen2($host, *READER, *WRITER, "./rmount_remote.pl $dir")
		or die "ssh: $!\n";
	select WRITER;
	$| = 1;
	select STDOUT;
}

$SIG{CHLD} = sub {
	use POSIX ":sys_wait_h";
	my $kid;
	do {
		$kid = waitpid(-1,WNOHANG);
	} until $kid < 1;
};

connect_remote;

sub netlink {
	my ($str) = Dumper(\@_)."\n";
	$str = sprintf("%08i\n%s",length($str),$str);
	while(1) { # retry as necessary
		my ($VAR1);
		$VAR1 = undef;
		eval {
			local $SIG{ALRM} = sub { die "timeout\n" };
			alarm 10;
			print WRITER $str;
			my ($len, $data);
			$data = '';
			if(sysread(READER,$len,9) == 9) {
				sysread(READER,$data,$len-length($data),length($data))
					while(length($data) < $len);
				eval $data;
			}
			alarm 0;
		};
		if(defined $VAR1) {
			return wantarray ? @{$VAR1} : $$VAR1[0];
		}
		print STDERR "failed to send command; reconnecting ssh\n";
		close(READER);
		close(WRITER);
		connect_remote();
	}
}

Filesys::Fuse3::main(%args);

netlink("bye");
close(READER);
close(WRITER);
