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`)
- 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
acknowledgement and decided to use incompatible escape codes for.

View File

@ -54,7 +54,12 @@ type it each time::
alias ssh="kitty +kitten ssh"
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
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]]:
other_ssh_args: List[str] = []
boolean_ssh_args: List[str] = []
@ -150,12 +177,7 @@ def quote(x: str) -> str:
return x
def main(args: List[str]) -> NoReturn:
ssh_args, server_args, passthrough = parse_ssh_args(args[1:])
if passthrough:
cmd = ['ssh'] + ssh_args + server_args
else:
terminfo = subprocess.check_output(['infocmp']).decode('utf-8')
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:])
@ -163,7 +185,36 @@ def main(args: List[str]) -> NoReturn:
else:
command_to_execute = ''
sh_script = sh_script.replace('EXEC_CMD', command_to_execute)
cmd = ['ssh'] + ssh_args + ['-t', server_args[0], sh_script] + server_args[1:]
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:
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:
cmd = ['ssh'] + ssh_args + server_args
else:
terminfo = subprocess.check_output(['infocmp']).decode('utf-8')
cmd = ['ssh'] + ssh_args
if use_posix:
cmd += get_posix_cmd(terminfo, server_args)
else:
cmd += get_python_cmd(terminfo, server_args)
os.execvp('ssh', cmd)