X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=cmd%2Fserver-cmd.py;h=a60bf2305eefaa1b9e5693a52affdb34628e4d91;hb=c40b3dd5fd74e72024fbaad3daf5a958aefa1c54;hp=a5e9abde18de074676fb4ddbc12a3ace734434f7;hpb=ccfa3bd44cd75ab4a8591a812618597effbe5bd9;p=bup.git diff --git a/cmd/server-cmd.py b/cmd/server-cmd.py index a5e9abd..a60bf23 100755 --- a/cmd/server-cmd.py +++ b/cmd/server-cmd.py @@ -1,11 +1,28 @@ -#!/usr/bin/env python -import os, sys, struct +#!/bin/sh +"""": # -*-python-*- +bup_python="$(dirname "$0")/bup-python" || exit $? +exec "$bup_python" "$0" ${1+"$@"} +""" +# end of bup preamble + +from __future__ import absolute_import +import os, sys, struct, subprocess + from bup import options, git -from bup.helpers import * +from bup.git import MissingObject +from bup.helpers import (Conn, debug1, debug2, linereader, lines_until_sentinel, + log) + suspended_w = None dumb_server_mode = False + +def do_help(conn, junk): + conn.write('Commands:\n %s\n' % '\n '.join(sorted(commands))) + conn.ok() + + def _set_mode(): global dumb_server_mode dumb_server_mode = os.path.exists(git.repo('bup-dumb-server')) @@ -13,22 +30,31 @@ def _set_mode(): % (dumb_server_mode and 'dumb' or 'smart')) +def _init_session(reinit_with_new_repopath=None): + if reinit_with_new_repopath is None and git.repodir: + return + git.check_repo_or_die(reinit_with_new_repopath) + # OK. we now know the path is a proper repository. Record this path in the + # environment so that subprocesses inherit it and know where to operate. + os.environ['BUP_DIR'] = git.repodir + debug1('bup server: bupdir is %r\n' % git.repodir) + _set_mode() + + def init_dir(conn, arg): git.init_repo(arg) debug1('bup server: bupdir initialized: %r\n' % git.repodir) - _set_mode() + _init_session(arg) conn.ok() def set_dir(conn, arg): - git.check_repo_or_die(arg) - debug1('bup server: bupdir is %r\n' % git.repodir) - _set_mode() + _init_session(arg) conn.ok() def list_indexes(conn, junk): - git.check_repo_or_die() + _init_session() suffix = '' if dumb_server_mode: suffix = ' load' @@ -39,7 +65,7 @@ def list_indexes(conn, junk): def send_index(conn, name): - git.check_repo_or_die() + _init_session() assert(name.find('/') < 0) assert(name.endswith('.idx')) idx = git.open_idx(git.repo('objects/pack/%s' % name)) @@ -50,7 +76,7 @@ def send_index(conn, name): def receive_objects_v2(conn, junk): global suspended_w - git.check_repo_or_die() + _init_session() suggested = set() if suspended_w: w = suspended_w @@ -95,7 +121,10 @@ def receive_objects_v2(conn, junk): assert(oldpack.endswith('.idx')) (dir,name) = os.path.split(oldpack) if not (name in suggested): - debug1("bup server: suggesting index %s\n" % name) + debug1("bup server: suggesting index %s\n" + % git.shorten_hash(name)) + debug1("bup server: because of object %s\n" + % shar.encode('hex')) conn.write('index %s\n' % name) suggested.add(name) continue @@ -111,31 +140,26 @@ def _check(w, expected, actual, msg): def read_ref(conn, refname): - git.check_repo_or_die() + _init_session() r = git.read_ref(refname) conn.write('%s\n' % (r or '').encode('hex')) conn.ok() def update_ref(conn, refname): - git.check_repo_or_die() + _init_session() newval = conn.readline().strip() oldval = conn.readline().strip() git.update_ref(refname, newval.decode('hex'), oldval.decode('hex')) conn.ok() - -cat_pipe = None -def cat(conn, id): - global cat_pipe - git.check_repo_or_die() - if not cat_pipe: - cat_pipe = git.CatPipe() +def join(conn, id): + _init_session() try: - for blob in cat_pipe.join(id): + for blob in git.cp().join(id): conn.write(struct.pack('!I', len(blob))) conn.write(blob) - except KeyError, e: + except KeyError as e: log('server: error: %s\n' % e) conn.write('\0\0\0\0') conn.error(e) @@ -143,6 +167,65 @@ def cat(conn, id): conn.write('\0\0\0\0') conn.ok() +def cat_batch(conn, dummy): + _init_session() + cat_pipe = git.cp() + # For now, avoid potential deadlock by just reading them all + for ref in tuple(lines_until_sentinel(conn, '\n', Exception)): + ref = ref[:-1] + it = cat_pipe.get(ref) + info = next(it) + if not info[0]: + conn.write('missing\n') + continue + conn.write('%s %s %d\n' % info) + for buf in it: + conn.write(buf) + conn.ok() + +def refs(conn, args): + limit_to_heads, limit_to_tags = args.split() + assert limit_to_heads in ('0', '1') + assert limit_to_tags in ('0', '1') + limit_to_heads = int(limit_to_heads) + limit_to_tags = int(limit_to_tags) + _init_session() + patterns = tuple(x[:-1] for x in lines_until_sentinel(conn, '\n', Exception)) + for name, oid in git.list_refs(patterns=patterns, + limit_to_heads=limit_to_heads, + limit_to_tags=limit_to_tags): + assert '\n' not in name + conn.write('%s %s\n' % (oid.encode('hex'), name)) + conn.write('\n') + conn.ok() + +def rev_list(conn, _): + _init_session() + count = conn.readline() + if not count: + raise Exception('Unexpected EOF while reading rev-list count') + count = None if count == '\n' else int(count) + fmt = conn.readline() + if not fmt: + raise Exception('Unexpected EOF while reading rev-list format') + fmt = None if fmt == '\n' else fmt[:-1] + refs = tuple(x[:-1] for x in lines_until_sentinel(conn, '\n', Exception)) + args = git.rev_list_invocation(refs, count=count, format=fmt) + p = subprocess.Popen(git.rev_list_invocation(refs, count=count, format=fmt), + preexec_fn=git._gitenv(git.repodir), + stdout=subprocess.PIPE) + while True: + out = p.stdout.read(64 * 1024) + if not out: + break + conn.write(out) + rv = p.wait() # not fatal + if rv: + msg = 'git rev-list returned error %d' % rv + conn.error(msg) + raise GitError(msg) + conn.ok() + optspec = """ bup server @@ -156,6 +239,8 @@ if extra: debug2('bup server: reading from stdin.\n') commands = { + 'quit': None, + 'help': do_help, 'init-dir': init_dir, 'set-dir': set_dir, 'list-indexes': list_indexes, @@ -163,7 +248,11 @@ commands = { 'receive-objects-v2': receive_objects_v2, 'read-ref': read_ref, 'update-ref': update_ref, - 'cat': cat, + 'join': join, + 'cat': join, # apocryphal alias + 'cat-batch' : cat_batch, + 'refs': refs, + 'rev-list': rev_list } # FIXME: this protocol is totally lame and not at all future-proof.