2 Connect to a remote host via SSH and execute a command on the host.
5 from __future__ import absolute_import, print_function
6 import sys, os, re, subprocess
8 from bup import helpers, path
9 from bup.compat import environ
11 def connect(rhost, port, subcmd, stderr=None):
12 """Connect to 'rhost' and execute the bup subcommand 'subcmd' on it."""
13 assert not re.search(br'[^\w-]', subcmd)
14 nicedir = re.sub(b':', b'_', path.exedir())
18 argv = [b'bup', subcmd]
20 # WARNING: shell quoting security holes are possible here, so we
21 # have to be super careful. We have to use 'sh -c' because
22 # csh-derived shells can't handle PATH= notation. We can't
23 # set PATH in advance, because ssh probably replaces it. We
24 # can't exec *safely* using argv, because *both* ssh and 'sh -c'
25 # allow shellquoting. So we end up having to double-shellquote
27 escapedir = re.sub(br'([^\w/])', br'\\\\\\\1', nicedir)
28 buglvl = helpers.atoi(environ.get(b'BUP_DEBUG'))
29 force_tty = helpers.atoi(environ.get(b'BUP_FORCE_TTY'))
31 sh -c PATH=%s:'$PATH BUP_DEBUG=%s BUP_FORCE_TTY=%s bup %s'
32 """ % (escapedir, buglvl, force_tty, subcmd)
35 argv.extend((b'-p', port))
36 argv.extend((rhost, b'--', cmd.strip()))
37 #helpers.log('argv is: %r\n' % argv)
41 envpath = environ.get(b'PATH')
43 env[b'PATH'] = nicedir if not envpath else nicedir + b':' + envpath
44 if sys.version_info[0] < 3:
45 return subprocess.Popen(argv,
46 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
49 preexec_fn=lambda: os.setsid())
51 return subprocess.Popen(argv,
52 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
55 start_new_session=True)