#!/usr/bin/perl

# Universal script to setup viewers for CSO remote observing

# supported systems 
# Mac OSX
# linux: vncviewer, krdc, vinagre

# $version = "  Version: 0.0  2013-11-05";
# $version = "  Version: 0.1  2014-01-22";
# $version = "  Version: 0.2  2014-03-10";
# $version = "  Version: 0.3  2014-03-24";
# $version = "  Version: 0.4  2014-06-24";
# $version = "  Version: 0.5  2014-07-02";
# $version = "  Version: 0.5.1  2014-07-07";
# $version = "  Version: 0.5.2  2014-08-25";
$version = "  Version: 0.5.3  2014-10-05";

# S. Radford

# Portions adapted from tunnelopen.pl
# Copyright (c) 2008 Thomas A. Fine
# Redistribution is permitted, in whole or in part, for commercial and
# non-commercial purposes, provided only that this copyright notice
# remains.

use Socket;

$timeout=30;
$startport=12345;
$maxtries=50;
$options="";
$user="";
$localhost="localhost";
$command="sleep $timeout";

$service = "vnc";
$ssh_host = "kilauea.caltech.edu";
$host="localhost";

# determine os type, available vncviewers
# $debug not defined yet

$ostype=$^O;
if ($debug) { print STDERR $ostype, "\n"; }

if ($ostype eq "linux") {

  if (length (`which vncviewer 2>/dev/null`) > 0) {
    $viewer = "vncviewer";
  } elsif (length (`which krdc 2>/dev/null`) > 0) {
    $viewer = "krdc";
  } elsif (length (`which vinagre 2>/dev/null`) > 0) {
    $viewer = "vinagre";
  } else {
    print STDERR "No supported viewer available \n";
    exit(-1);
  }
  if ($debug) { print STDERR $viewer, "\n"; }

} elsif ( $ostype ne "darwin" ) {   # Mac OS X
  print STDERR "Unsupported system ", $ostype, "\n";
  exit(-1);
}

# parse options

while (substr($ARGV[0],0,1) eq "-") {
  $opt=shift(@ARGV);

  if ($opt eq "-l" || $opt eq "--login") {
    $user = shift(@ARGV) . "@";
  }
  elsif ($opt eq "-ll" ) {
    $user= getpwuid($<) . "@";
  }
  elsif ($opt eq "-b" || $opt eq "--debug") {
    $debug=1;
  }
  elsif ($opt eq "-t" || $opt eq "--timeout") {
    if (length($command) == 0) {
      &usage;
      exit(-1);
    }
    $timeout=shift(@ARGV);
    if ($timeout !~ /^[0-9]+[hmd]?$/) {
      &usage;
      exit(-1);
    }
    if ($timeout =~ /[hmd]$/) {
      $u=substr($timeout,length($timeout)-1,1);
      $timeout=substr($timeout,0,length($timeout)-1);
      if ($u eq "m") { $timeout *= 60; }
      if ($u eq "h") { $timeout *= 3600; }
      if ($u eq "d") { $timeout *= 86400; }
    }
    $command="sleep $timeout";
    ++$customtimeout;
  }
  elsif ($opt eq "-v" || $opt eq "--viewer") {
    $viewer = shift(@ARGV);
    if (($ostype ne 'linux') or (($viewer ne "vncviewer") and
        ($viewer ne "krdc") and ($viewer ne "vinagre") ) ) {
      print STDERR "Viewer ", $viewer, " not supported \n";
      exit(-1);
    }
    if (length (`which $viewer 2>/dev/null`) == 0 ) {
      print STDERR "Viewer ", $viewer, " not available \n";
      exit(-1);
    }    
  }
  elsif ($opt eq "-C" || $opt eq "--comp") {
    $options .= "-C ";
  }
  elsif ($opt eq "-h" || $opt eq "--help") {
    &usage;
    exit(0);
  }
}

if ($#ARGV == -1 || $#ARGV > 0) {
  &usage;
  exit(0);
}

# parse function to assign port number

$function=shift(@ARGV);

if ($debug) { print STDERR "function: ", $function, "\n"; }

if ($function+0 eq $function) {                       # numeric
  if (int($function) == $function) {                  # integer
    if ($function >= 5900 && $function <= 5999) {
      $port = $function;
    } elsif ($function >= 0 && $function <= 99) {
      $port = $function + 5900;
    } else {
      &usage;
      exit(0);
    }
  } else {
    &usage;
    exit(0);
  }
} else {
  if ($function eq 'heterodyne') {
    $port = 5901;
  } elsif ($function eq 'spectro') {
    $port = 5901;
  } elsif ($function eq 'uip') {
    $port = 5902;
  } elsif ($function eq 'dsos') {
    $port = 5903;
  } elsif ($function eq 'bolocam') {
    $port = 5906;
    if ($user eq "") { $user= "bolocam@"; }
  } elsif ($function eq 'bolocam.ql') {
    $port = 5907;
    if ($user eq "") { $user= "bolocam@"; }
  } elsif ($function eq 'music.daq') {
    $port = 5907;
    if ($user eq "") { $user= "bolocam@"; }
  } elsif ($function eq 'music') {
    $port = 5908;
    if ($user eq "") { $user= "bolocam@"; }
  } elsif ($function eq 'music.ql') {
    $port = 5909;
    if ($user eq "") { $user= "bolocam@"; }
  } elsif ($function eq 'sharc') {
    $port = 5911;
    if ($user eq "") { $user= "sharc@"; }
  } elsif ($function eq 'sharc.ql') {
    $port = 5912;
    if ($user eq "") { $user= "sharc@"; }
  } elsif ($function eq 'sharc.fridge') {
    $port = 5913;
    if ($user eq "") { $user= "sharc@"; }
  } elsif ($function eq 'zspec') {
    $port = 5915;
    if ($user eq "") { $user= "zspec@"; }
  } elsif ($function eq 'zspec.ql') {
    $port = 5916;
    if ($user eq "") { $user= "zspec@"; }
  } elsif ($function eq 'test') {
    $port = 5999;
  } else {
    &usage;
    exit(0);
  }
}

if ($user eq "") { $user= getpwuid($<) . "@"; }

$remoteport=$port; 

if ($debug) { print STDERR "port: ", $port, "\n"; }

# if (length($ssh_host) == 0) { $ssh_host=$host; }

$proto = getprotobyname('tcp');
if (!socket(S,PF_INET,SOCK_STREAM,$proto)) {
  print STDERR "Can't create socket: $!\n";
  exit(-1);
}

for ($i=$startport; $i<$startport+$maxtries; ++$i) {
  $here=sockaddr_in($i,inet_aton($localhost));
  if (bind(S, $here)) {
    if ($debug) { print STDERR "using local port $i\n"; }
    $localport=$i;
    #this shouldn't cause TIME_WAIT, because it isn't connected or listening
    close(S);
    last;
  }
}
if ($localport == 0) {
  print STDERR "Can't find an available intermediate port!\n";
  exit(-1);
}

# open ssh tunnel

if ($debug) { print STDERR 
  "ssh -L $localport:$host:$remoteport -f $options $user$ssh_host $command\n"; }
print "ssh $user$ssh_host\n";
system("ssh -L $localport:$host:$remoteport -f $options $user$ssh_host $command");

if ($ostype eq "darwin") {

  if ($debug) { print STDERR "open $service://$localhost:$localport\n"; }
  system("open $service://$localhost:$localport");

} elsif ($ostype eq "linux") {

  if ($viewer eq "vncviewer") {          # variant syntax

    if ($debug) { print STDERR "$viewer -shared $localhost\:\:$localport & \n"; }
    if ($debug) {
      system("$viewer -shared $localhost\:\:$localport &");
    } else {
    system("$viewer -shared $localhost\:\:$localport 2> /dev/null &");
    }

  } else {

    if ($debug) { print STDERR "$viewer $localhost:$localport & \n"; }
    system("$viewer $localhost:$localport &");

  }

} else {
  print STDERR "Unsupported system ", $ostype, "\n";
  &usage;
  exit(-1);
}

#
# END OF MAIN
#

sub usage {
  print <<EEE;
Usage: $0 [options] {N, P, function}
  N - VNC desktop number, 1 <= N <= 99
  P - VNC port number, 5901 <= P <= 5999
  function - observing desktop function
    uip, spectro, heterodyne,  dsos,
    bolocam, bolocam.ql,  
    music, music.daq, music.ql,
    sharc, sharc.fridge, sharc.ql
    zspec, zspec.ql
  Options are:
  -C/--comp             Use ssh compression.
  -h/--help             Show this message.
  -l/--login user       Specifies the login username for the remote system.
                          Some function use default usernames.
			  If login is not specified it uses your current login name.
  -t/--timeout          Keeps the ssh tunnel running for at least timeout
			  seconds. Defaults to 30, to leave time for VNC
			  viewer setup. Can be set to longer values
			  but will automatically exit eventually. May also be 
                          specified in minutes, hours or days, by adding an m, h, or d
			  at the end of the number, e.g. "-t 3h"
  -v/--viewer viewer    Specifies an alternate VNC viewer. linux only. Supported
                          viewers are vncviewer, krdc, and vinagre.
EEE
  print $version, "\n";
}