]> arthur.barton.de Git - bup.git/commitdiff
Teach bup about URLs and non-ssh remotes
authorBrandon Low <lostlogic@lostlogicx.com>
Wed, 5 Jan 2011 17:28:25 +0000 (09:28 -0800)
committerAvery Pennarun <apenwarr@gmail.com>
Thu, 6 Jan 2011 23:23:59 +0000 (15:23 -0800)
Also adds the ability to connect to ports other than default for ssh.

Signed-off-by: Brandon Low <lostlogic@lostlogicx.com>
cmd/on-cmd.py
lib/bup/client.py
lib/bup/ssh.py
lib/bup/t/tclient.py

index da7be3757ff32904718f8bd48c1313de58c9f2b8..c40a6e8de90ddb262153e4920e31bcbc8edf949f 100755 (executable)
@@ -28,9 +28,14 @@ p = None
 ret = 99
 
 try:
-    hostname = extra[0]
+    hp = extra[0].split(':')
+    if len(hp) == 1:
+        (hostname, port) = (hp[0], None)
+    else:
+        (hostname, port) = hp
+
     argv = extra[1:]
-    p = ssh.connect(hostname, 'on--server')
+    p = ssh.connect(hostname, port, 'on--server')
 
     argvs = '\0'.join(['bup'] + argv)
     p.stdin.write(struct.pack('!I', len(argvs)) + argvs)
index d47cc7537f6711dfe16eda4460d5b943b1b09ae7..f75fab6064cba76761d15c1b03c5620e42355b90 100644 (file)
@@ -30,41 +30,62 @@ def _raw_write_bwlimit(f, buf, bwcount, bwtime):
             bwcount = len(sub)  # might be less than 4096
             bwtime = next
         return (bwcount, bwtime)
-                       
+
+
+def parse_remote(remote):
+    protocol = r'([a-z]+)://'
+    host = r'(?P<sb>\[)?((?(sb)[0-9a-f:]+|[^:/]+))(?(sb)\])'
+    port = r'(?::(\d+))?'
+    path = r'(/.*)?'
+    url_match = re.match(
+            '%s(?:%s%s)?%s' % (protocol, host, port, path), remote, re.I)
+    if url_match:
+        assert(url_match.group(1) in ('ssh', 'bup', 'file'))
+        return url_match.group(1,3,4,5)
+    else:
+        rs = remote.split(':', 1)
+        if len(rs) == 1 or rs[0] in ('', '-'):
+            return 'file', None, None, rs[-1]
+        else:
+            return 'ssh', rs[0], None, rs[1]
+
 
 class Client:
     def __init__(self, remote, create=False):
-        self._busy = self.conn = self.p = self.pout = self.pin = None
+        self._busy = self.conn = None
+        self.sock = self.p = self.pout = self.pin = None
         is_reverse = os.environ.get('BUP_SERVER_REVERSE')
         if is_reverse:
             assert(not remote)
             remote = '%s:' % is_reverse
-        rs = remote.split(':', 1)
-        if len(rs) == 1:
-            (host, dir) = (None, remote)
-        else:
-            (host, dir) = rs
-        (self.host, self.dir) = (host, dir)
+        (self.protocol, self.host, self.port, self.dir) = parse_remote(remote)
         self.cachedir = git.repo('index-cache/%s'
                                  % re.sub(r'[^@\w]', '_', 
-                                          "%s:%s" % (host, dir)))
+                                          "%s:%s" % (self.host, self.dir)))
         if is_reverse:
             self.pout = os.fdopen(3, 'rb')
             self.pin = os.fdopen(4, 'wb')
         else:
-            try:
-                self.p = ssh.connect(host, 'server')
-                self.pout = self.p.stdout
-                self.pin = self.p.stdin
-            except OSError, e:
-                raise ClientError, 'connect: %s' % e, sys.exc_info()[2]
+            if self.protocol in ('ssh', 'file'):
+                try:
+                    # FIXME: ssh and file shouldn't use the same module
+                    self.p = ssh.connect(self.host, self.port, 'server')
+                    self.pout = self.p.stdout
+                    self.pin = self.p.stdin
+                except OSError, e:
+                    raise ClientError, 'connect: %s' % e, sys.exc_info()[2]
+            elif self.protocol == 'bup':
+                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                self.sock.connect((self.host, self.port or 1982))
+                self.pout = self.sock.makefile('rb')
+                self.pin = self.sock.makefile('wb')
         self.conn = Conn(self.pout, self.pin)
-        if dir:
-            dir = re.sub(r'[\r\n]', ' ', dir)
+        if self.dir:
+            self.dir = re.sub(r'[\r\n]', ' ', self.dir)
             if create:
-                self.conn.write('init-dir %s\n' % dir)
+                self.conn.write('init-dir %s\n' % self.dir)
             else:
-                self.conn.write('set-dir %s\n' % dir)
+                self.conn.write('set-dir %s\n' % self.dir)
             self.check_ok()
         self.sync_indexes()
 
@@ -85,13 +106,15 @@ class Client:
             while self.pout.read(65536):
                 pass
             self.pout.close()
+        if self.sock:
+            self.sock.close()
         if self.p:
             self.p.wait()
             rv = self.p.wait()
             if rv:
                 raise ClientError('server tunnel returned exit code %d' % rv)
         self.conn = None
-        self.p = self.pin = self.pout = None
+        self.sock = self.p = self.pin = self.pout = None
 
     def check_ok(self):
         if self.p:
index 52ee03584c53171f9fa3407958d1b55fed71b2c1..f91e16dcb34bc9ce1d755704b2c1a8f96db439b3 100644 (file)
@@ -9,7 +9,7 @@ import subprocess
 from bup import helpers
 
 
-def connect(rhost, subcmd):
+def connect(rhost, port, subcmd):
     """Connect to 'rhost' and execute the bup subcommand 'subcmd' on it."""
     assert(not re.search(r'[^\w-]', subcmd))
     main_exe = os.environ.get('BUP_MAIN_EXE') or sys.argv[0]
@@ -33,7 +33,10 @@ def connect(rhost, subcmd):
         cmd = r"""
                    sh -c PATH=%s:'$PATH BUP_DEBUG=%s BUP_FORCE_TTY=%s bup %s'
                """ % (escapedir, buglvl, force_tty, subcmd)
-        argv = ['ssh', rhost, '--', cmd.strip()]
+        argv = ['ssh']
+        if port:
+            argv.extend(('-p', port))
+        argv.extend((rhost, '--', cmd.strip()))
         #helpers.log('argv is: %r\n' % argv)
     def setup():
         # runs in the child process
index a3553b69a4b0dace317165808415efdfb50570cf..64aa360cc04f7e2904f8c63373d1e61687333871 100644 (file)
@@ -73,3 +73,23 @@ def test_midx_refreshing():
     WVPASSEQ(len(pi.packs), 2)
     pi.refresh(skip_midx=False)
     WVPASSEQ(len(pi.packs), 1)
+
+@wvtest
+def test_remote_parsing():
+    tests = (
+        (':/bup', ('file', None, None, '/bup')),
+        ('file:///bup', ('file', None, None, '/bup')),
+        ('192.168.1.1:/bup', ('ssh', '192.168.1.1', None, '/bup')),
+        ('ssh://192.168.1.1:2222/bup', ('ssh', '192.168.1.1', '2222', '/bup')),
+        ('ssh://[ff:fe::1]:2222/bup', ('ssh', 'ff:fe::1', '2222', '/bup')),
+        ('bup://foo.com:1950', ('bup', 'foo.com', '1950', None)),
+        ('bup://foo.com:1950/bup', ('bup', 'foo.com', '1950', '/bup')),
+        ('bup://[ff:fe::1]/bup', ('bup', 'ff:fe::1', None, '/bup')),
+    )
+    for remote, values in tests:
+        WVPASSEQ(client.parse_remote(remote), values)
+    try:
+        client.parse_remote('http://asdf.com/bup')
+        WVFAIL()
+    except AssertionError:
+        WVPASS()