#!/usr/bin/perl # Connect via SSH to a remote machine and open a vnc tunnel, then start # vncviewer locally connected to that tunnel. Destroy the tunnel when # vncviewer exits # # Example usage: # svnc -p 10100 cpd@aerolab1 # Would connect on port 10100 to aerolab1 and log in as cpd # 050307 DCH Added scanning for an avaiable VNC display # This version uses pipes and a scheme of detecting the start sequence that # doesn't involve a temporary file and polling. use POSIX qw(dup2); if (@ARGV < 1) { print "Usage: svnc \tOpen a VNC tunnel via SSH and start a local vncviewer on that tunnel \t are arguments as ssh(1). One of which must specify \twhere to connect to\n"; # exit 1; } # First we need to find an avaiable VNC display, locally $VNC_PORT = 5901; if (open(NSPIPE, 'netstat -l -n --inet |')) { my %open_ports; while () { next if (!/^tcp\s+\d+\s+\d+\s+\d+\.\d+\.\d+\.\d+\:(\d+)\s+/); $open_ports{$1} = 1; } my $i; $VNC_PORT = -1; for ($i = 5901; $i < 7000; $i++) { if (!defined($open_ports{$i}) || $open_ports{$i} != 1) { $VNC_PORT = $i; last; } } if ($VNC_PORT == -1) { print "Unable to locate a free VNC display\n"; exit 1; } } else { print "Unable to start netstat, using default VNC display: $!\n"; } $SEARCHSTR="TUNNELESTABLISHED"; my $ssh_pid = 0; # Kill the SSH process and wait on its zombie sub do_disconnect { # Terminate our child, if it's running if ($ssh_pid != 0) { kill 2, $ssh_pid; waitpid $ssh_pid, 0; $ssh_pid = 0; } } # Handle an abort signal sub abort_signal { do_disconnect; print "Aborted by signal\n"; exit 1 } # Set signal handler $SIG{INT} = \&do_disconnect; $SIG{TERM} = \&do_disconnect; $SIG{QUIT} = \&do_disconnect; # Get the pipe pipe (RDPIPE, WRPIPE) or die "Unable to create pipe: $!"; # And create our child $fret = fork; if ($fret == 0) { # We're the child my @args; # Setup the args array # This must be able to execute on both linux and windows (with the sleep # program in /aer/prg/win32/sleep.exe placed in %WINDOWS%/System[32]) # Thus the embeded newline is CRITICAL, windows cmd.exe doesn't # support other command delimiters (bah...) push @args, "-T", "-x", "-C", "-L", "$VNC_PORT:localhost:5900"; push @args, @ARGV; push @args, "echo $SEARCHSTR; sleep 30"; # Close the end of the pipe we don't use close RDPIPE; # Redirect the writing end to STDOUT dup2(fileno(WRPIPE), 1); # We're done with the old pipe FH, close it now close WRPIPE; # Execute ssh with the given arguments exec "ssh", @args; die "Unable to exec ssh: $!"; } elsif ($fret < 0) { die "Unable to fork: $!"; } # We're the parent # Save the child PID $ssh_pid = $fret; # Close our un-used pipe end close WRPIPE; # Copy the search string, but blank it, so we have the right length my $currentstr = $SEARCHSTR; $currentstr =~ s/./ /g; my $ch; my $found = 0; while (defined($ch = getc RDPIPE)) { # Shift in the new character (by appending it, then deleteing the first) $currentstr = $currentstr . $ch; $currentstr =~ s/^.//; if ($currentstr eq $SEARCHSTR) { $found = 1; last; } } # Most likely cause is that ssh exited without printing the sequence if (!$found) { do_disconnect; print "Never got connection confirmation string, perhaps a problem connecting?\n"; exit 1; } my $vnc_display = $VNC_PORT - 5900; # Launch the viewer system "vncviewer :$vnc_display" or print "Error executing vncviewer: $!\n"; # Termiante SSH when the viewer is closed do_disconnect; exit 0;