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