ssh kitten: Allow using python instead of the shell on the server

This commit is contained in:
Kovid Goyal 2021-01-31 09:04:46 +05:30
parent ee198ca863
commit e9e8ef7210
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 68 additions and 9 deletions

View File

@ -13,6 +13,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- diff kitten: Implement recursive diff over SSH (:iss:`3268`) - diff kitten: Implement recursive diff over SSH (:iss:`3268`)
- ssh kitten: Allow using python instead of the shell on the server, useful if
the shell used is a non-POSIX compliant one, such as fish (:iss:`3277`)
- Add support for the color settings stack that XTerm copied from us without - Add support for the color settings stack that XTerm copied from us without
acknowledgement and decided to use incompatible escape codes for. acknowledgement and decided to use incompatible escape codes for.

View File

@ -54,7 +54,12 @@ type it each time::
alias ssh="kitty +kitten ssh" alias ssh="kitty +kitten ssh"
If for some reason that does not work (typically because the server is using a If for some reason that does not work (typically because the server is using a
non POSIX compliant shell), you can use the following one-liner instead (it non POSIX compliant shell), you can try using it with python instead::
kitty +kitten ssh use-python myserver
If that also fails, perhaps because python is not installed on the remote
server, use the following one-liner instead (it
is slower as it needs to ssh into the server twice, but will work with most is slower as it needs to ssh into the server twice, but will work with most
servers):: servers)::

View File

@ -46,6 +46,33 @@ exec -a "-$shell_name" "$0"
''' '''
PYTHON_SCRIPT = '''\
#!/usr/bin/env python
from __future__ import print_function
from tempfile import NamedTemporaryFile
import subprocess, os, sys, pwd, binascii, json
# macOS ships with an ancient version of tic that cannot read from stdin, so we
# create a temp file for it
with NamedTemporaryFile() as tmp:
tmp.write(binascii.unhexlify('{terminfo}'))
p = subprocess.Popen(['tic', '-x', '-o', os.path.expanduser('~/.terminfo'), tmp.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.wait() != 0:
getattr(sys.stderr, 'buffer', sys.stderr).write(stdout + stderr)
raise SystemExit('Failed to compile terminfo using tic')
command_to_execute = json.loads(binascii.unhexlify('{command_to_execute}'))
if command_to_execute:
os.execlp(command_to_execute[0], *command_to_execute)
try:
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
except KeyError:
shell_path = '/bin/sh'
shell_name = '-' + os.path.basename(shell_path)
os.execlp(shell_path, shell_name)
'''
def get_ssh_cli() -> Tuple[Set[str], Set[str]]: def get_ssh_cli() -> Tuple[Set[str], Set[str]]:
other_ssh_args: List[str] = [] other_ssh_args: List[str] = []
boolean_ssh_args: List[str] = [] boolean_ssh_args: List[str] = []
@ -150,20 +177,44 @@ def quote(x: str) -> str:
return x return x
def get_posix_cmd(terminfo: str, server_args: List[str]) -> List[str]:
sh_script = SHELL_SCRIPT.replace('TERMINFO', terminfo, 1)
if len(server_args) > 1:
command_to_executeg = (quote(c) for c in server_args[1:])
command_to_execute = 'exec ' + ' '.join(command_to_executeg)
else:
command_to_execute = ''
sh_script = sh_script.replace('EXEC_CMD', command_to_execute)
return ['-t', server_args[0], sh_script] + server_args[1:]
def get_python_cmd(terminfo: str, server_args: List[str]) -> List[str]:
import json
hostname = server_args[0]
command_to_execute = server_args[1:]
script = PYTHON_SCRIPT.format(
terminfo=terminfo.encode('utf-8').hex(),
command_to_execute=json.dumps(command_to_execute).encode('utf-8').hex()
)
return ['-t', hostname, f'python -c "{script}"']
def main(args: List[str]) -> NoReturn: def main(args: List[str]) -> NoReturn:
ssh_args, server_args, passthrough = parse_ssh_args(args[1:]) args = args[1:]
use_posix = True
if args and args[0] == 'use-python':
args = args[1:]
use_posix = False
ssh_args, server_args, passthrough = parse_ssh_args(args)
if passthrough: if passthrough:
cmd = ['ssh'] + ssh_args + server_args cmd = ['ssh'] + ssh_args + server_args
else: else:
terminfo = subprocess.check_output(['infocmp']).decode('utf-8') terminfo = subprocess.check_output(['infocmp']).decode('utf-8')
sh_script = SHELL_SCRIPT.replace('TERMINFO', terminfo, 1) cmd = ['ssh'] + ssh_args
if len(server_args) > 1: if use_posix:
command_to_executeg = (quote(c) for c in server_args[1:]) cmd += get_posix_cmd(terminfo, server_args)
command_to_execute = 'exec ' + ' '.join(command_to_executeg)
else: else:
command_to_execute = '' cmd += get_python_cmd(terminfo, server_args)
sh_script = sh_script.replace('EXEC_CMD', command_to_execute)
cmd = ['ssh'] + ssh_args + ['-t', server_args[0], sh_script] + server_args[1:]
os.execvp('ssh', cmd) os.execvp('ssh', cmd)