sshuttle/ssh.py
Avery Pennarun 65b0390fe9 ssh.py: use 'exec python -c' instead of just 'python -c'.
This gets rid of an extra intermediate sh process on the server that we were
keeping for no good reason, since it would exit as soon as python exited
anyway.
2011-05-03 13:51:09 -07:00

107 lines
3.3 KiB
Python

import sys, os, re, socket, zlib
import compat.ssubprocess as ssubprocess
import helpers
from helpers import *
def readfile(name):
basedir = os.path.dirname(os.path.abspath(sys.argv[0]))
path = [basedir] + sys.path
for d in path:
fullname = os.path.join(d, name)
if os.path.exists(fullname):
return open(fullname, 'rb').read()
raise Exception("can't find file %r in any of %r" % (name, path))
def empackage(z, filename, data=None):
(path,basename) = os.path.split(filename)
if not data:
data = readfile(filename)
content = z.compress(data)
content += z.flush(zlib.Z_SYNC_FLUSH)
return '%s\n%d\n%s' % (basename, len(content), content)
def connect(ssh_cmd, rhostport, python, stderr, options):
main_exe = sys.argv[0]
portl = []
rhostIsIPv6 = False
if (rhostport or '').count(':') > 1:
rhostIsIPv6 = True
if rhostport.count(']') or rhostport.count('['):
result = rhostport.split(']')
rhost = result[0].strip('[')
if len(result) > 1:
result[1] = result[1].strip(':')
if result[1] is not '':
portl = ['-p', str(int(result[1]))]
else: # can't disambiguate IPv6 colons and a port number. pass the hostname through.
rhost = rhostport
else: # IPv4
l = (rhostport or '').split(':', 1)
rhost = l[0]
if len(l) > 1:
portl = ['-p', str(int(l[1]))]
if rhost == '-':
rhost = None
ipv6flag = []
if rhostIsIPv6:
ipv6flag = ['-6']
z = zlib.compressobj(1)
content = readfile('assembler.py')
optdata = ''.join("%s=%r\n" % (k,v) for (k,v) in options.items())
content2 = (empackage(z, 'cmdline_options.py', optdata) +
empackage(z, 'helpers.py') +
empackage(z, 'compat/ssubprocess.py') +
empackage(z, 'ssnet.py') +
empackage(z, 'hostwatch.py') +
empackage(z, 'server.py') +
"\n")
pyscript = r"""
import sys;
skip_imports=1;
verbosity=%d;
exec compile(sys.stdin.read(%d), "assembler.py", "exec")
""" % (helpers.verbose or 0, len(content))
pyscript = re.sub(r'\s+', ' ', pyscript.strip())
if not rhost:
# ignore the --python argument when running locally; we already know
# which python version works.
argv = [sys.argv[1], '-c', pyscript]
else:
if ssh_cmd:
sshl = ssh_cmd.split(' ')
else:
sshl = ['ssh']
if python:
pycmd = "'%s' -c '%s'" % (python, pyscript)
else:
pycmd = ("P=python2; $P -V 2>/dev/null || P=python; "
"exec \"$P\" -c '%s'") % pyscript
argv = (sshl +
portl +
ipv6flag +
[rhost, '--', pycmd])
(s1,s2) = socket.socketpair()
def setup():
# runs in the child process
s2.close()
s1a,s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
s1.close()
debug2('executing: %r\n' % argv)
p = ssubprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup,
close_fds=True, stderr=stderr)
os.close(s1a)
os.close(s1b)
s2.sendall(content)
s2.sendall(content2)
return p, s2