#!/usr/bin/env python
# @package      hubzero-mw2-exec-virtualssh
# @file         /usr/bin/virtualssh_client
# @author       Pascal Meunier <pmeunier@purdue.edu>
# @copyright    Copyright (c) 2016-2017 HUBzero Foundation, LLC.
# @license      http://opensource.org/licenses/MIT MIT
#
# Based on prior work by Richard L. Kennell
#
# Copyright (c) 2016-2017 HUBzero Foundation, LLC.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# HUBzero is a registered trademark of HUBzero Foundation, LLC.
#

"""This runs on the execution host.  Its goal is to connect to an SSH server
running inside a tool container.   
1. Is there an SSH server?  If not call the script /usr/bin/virtualsshd_start
It will do the following:
Start an SSH server inside the container, listening on port 2200, running as the user.  
Use the configuration file /etc/mw-virtualssh/sshd_config.
Create SSH keys.  Copy public key to "/ssh/authorized_keys"
Call ensure-known-host to add container IP and SSH key to /etc/ssh/ssh_known_hosts

2. SSH into the container, redirecting stdin and out


To debug virtual SSH, go inside the container and start the server manually with:
su <username>
/usr/sbin/sshd -f /ssh/sshd_config -d -d

and from the execution host, run:
ssh -F /etc/mw-virtualssh/exec_ssh_config -i /etc/mw-virtualssh/ssh_guest_key -t -p 2200 username@ip.ad.dre.ss -v -v -v 
where ip.ad.dre.ss is the container's private IP and username is the user account for whom the tool session is running

"""
import os
import sys
import socket
import subprocess
import re
import time
import syslog

from hubzero.mw.constants import EXEC_CONFIG_FILE, VIRTUALSSH_K, USER_REGEXP
from hubzero.mw.container import Container

CONTAINER_CONF = {}
execfile(EXEC_CONFIG_FILE)

VSSH_PROXY_LOG = '/var/log/vssh-exec-proxy/vssh-exec-proxy.log'
idfile = "/etc/mw-virtualssh/ssh_guest_key"
ENSURE_PATH = "/usr/bin/ensure-known-host"
SSH_CONFIG_PATH = '/etc/mw-virtualssh/exec_ssh_config'
# vsshd_start creates the ssh keys if necessary, and copies them to the container
# it also starts the ssh server inside the container with the user's id
VSSHD_START_PATH = '/usr/bin/vsshd_start'

try:
  dispnum = int(sys.argv[1])
except ValueError:
  syslog.syslog("vssh_exec_proxy: Unable to determine display number")
  sys.exit(1)

try:
  username = sys.argv[2]
except (TypeError, ValueError):
  syslog.syslog("vssh_exec_proxy: Unable to determine username")
  sys.exit(1)

m = re.match(USER_REGEXP, username)
if m is None:
  syslog.syslog("vssh_exec_proxy: Bad username '%s'" % username)
  sys.exit(1)

if dispnum <= 0 or dispnum > 65535:
  syslog.syslog("vssh_exec_proxy: Bad container id '%d'" % dispnum)
  sys.exit(1)

# access control should have been done by front virtualssh proxy, we have no way of doing access control
syslog.syslog("vssh_exec_proxy: connect to container %d with username '%s'" % (dispnum, username))

# use Container class to make sure IP address is always calculated the same way
# (disp, machine_number, overrides={})
vps = Container(dispnum, 0, CONTAINER_CONF)
addr = vps.veaddr

# all arguments must be strings, so use "%d" % dispnum
# try several times to avoid "No passwd entry for user 'xyz'" messages in new containers
i=0
process = subprocess.Popen([VSSHD_START_PATH, "%d" % dispnum, username], stderr = subprocess.PIPE)
process.communicate()
rcode = process.returncode
while i <100 and rcode != 0:
  process = subprocess.Popen([VSSHD_START_PATH, "%d" % dispnum, username], stderr = subprocess.PIPE)
  process.communicate()
  rcode = process.returncode
  # print "sleeping 1 second"
  time.sleep(1)
  i+=1

if rcode != 0:
  syslog.syslog("vssh_exec_proxy: Unable to start or setup an SSH server inside the tool session (is the session still running?)")
  print "Unable to start or setup an SSH server inside the tool session (is the session still running?)"
  process = subprocess.Popen([VSSHD_START_PATH, "%d" % dispnum, username], stderr = subprocess.PIPE)
  syslog.syslog("vssh_exec_proxy: %s %s" % process.communicate())
  sys.exit(1)

# messing with the known_hosts doesn't help communications within the execution host
# so we have "StrictHostKeyChecking no"
# without that we'd always be copying the key with full trust anyway
# subprocess.check_call([ENSURE_PATH, addr, '2200'])

flags=[]
if os.environ.has_key('SSH_TTY'):
  flags += [ '-t' ]
if os.environ.has_key('SSH_AUTH_SOCK'):
  flags += [ '-A' ]
if os.environ.has_key('DISPLAY'):
  flags += [ '-X', '-Y' ]

command = str.join(' ',sys.argv[3:])
#print "ssh-client command is '%s'" % command
#print "flags are ",
#print flags
#print "/usr/bin/ssh %s %s %s %s %s %s %s %s %s %s" % ('ssh', '-F', SSH_CONFIG_PATH, '-i', idfile,flags,'-p', '2200', '%s@%s' % (username,addr), command)

#os.system('env')
#time.sleep(.5)
pid = os.fork()
if pid == 0:
  os.environ['VDISPLAY'] = '%s:0.0' % addr
  os.execve("/usr/bin/ssh",
            ['ssh', '-F', SSH_CONFIG_PATH, '-i', idfile ] + flags +
            [ '-p', '2200', '%s@%s' % (username,addr), command],
            os.environ)
else:
  os.wait()

sys.exit(0)

