]> arthur.barton.de Git - bup.git/blob - cmd-server.py
5ca8f36532dc617635eecb8cb51cf9ef12df339c
[bup.git] / cmd-server.py
1 #!/usr/bin/env python
2 import sys, struct, mmap
3 import options, git
4 from helpers import *
5
6 suspended_w = None
7
8
9 def init_dir(conn, arg):
10     git.init_repo(arg)
11     log('bup server: bupdir initialized: %r\n' % git.repodir)
12     conn.ok()
13
14
15 def set_dir(conn, arg):
16     git.check_repo_or_die(arg)
17     log('bup server: bupdir is %r\n' % git.repodir)
18     conn.ok()
19
20     
21 def list_indexes(conn, junk):
22     git.check_repo_or_die()
23     for f in os.listdir(git.repo('objects/pack')):
24         if f.endswith('.idx'):
25             conn.write('%s\n' % f)
26     conn.ok()
27
28
29 def send_index(conn, name):
30     git.check_repo_or_die()
31     assert(name.find('/') < 0)
32     assert(name.endswith('.idx'))
33     idx = git.PackIndex(git.repo('objects/pack/%s' % name))
34     conn.write(struct.pack('!I', len(idx.map)))
35     conn.write(idx.map)
36     conn.ok()
37
38
39 def receive_objects(conn, junk):
40     global suspended_w
41     git.check_repo_or_die()
42     suggested = {}
43     if suspended_w:
44         w = suspended_w
45         suspended_w = None
46     else:
47         w = git.PackWriter()
48     while 1:
49         ns = conn.read(4)
50         if not ns:
51             w.abort()
52             raise Exception('object read: expected length header, got EOF\n')
53         n = struct.unpack('!I', ns)[0]
54         #log('expecting %d bytes\n' % n)
55         if not n:
56             log('bup server: received %d object%s.\n' 
57                 % (w.count, w.count!=1 and "s" or ''))
58             fullpath = w.close()
59             (dir, name) = os.path.split(fullpath)
60             conn.write('%s.idx\n' % name)
61             conn.ok()
62             return
63         elif n == 0xffffffff:
64             log('bup server: receive-objects suspended.\n')
65             suspended_w = w
66             conn.ok()
67             return
68             
69         buf = conn.read(n)  # object sizes in bup are reasonably small
70         #log('read %d bytes\n' % n)
71         if len(buf) < n:
72             w.abort()
73             raise Exception('object read: expected %d bytes, got %d\n'
74                             % (n, len(buf)))
75         (type, content) = git._decode_packobj(buf)
76         sha = git.calc_hash(type, content)
77         oldpack = w.exists(sha)
78         if oldpack:
79             assert(oldpack.endswith('.idx'))
80             (dir,name) = os.path.split(oldpack)
81             if not (name in suggested):
82                 log("bup server: suggesting index %s\n" % name)
83                 conn.write('index %s\n' % name)
84                 suggested[name] = 1
85         else:
86             w._raw_write([buf])
87     # NOTREACHED
88
89
90 def read_ref(conn, refname):
91     git.check_repo_or_die()
92     r = git.read_ref(refname)
93     conn.write('%s\n' % (r or '').encode('hex'))
94     conn.ok()
95
96
97 def update_ref(conn, refname):
98     git.check_repo_or_die()
99     newval = conn.readline().strip()
100     oldval = conn.readline().strip()
101     git.update_ref(refname, newval.decode('hex'), oldval.decode('hex'))
102     conn.ok()
103
104
105 def cat(conn, id):
106     git.check_repo_or_die()
107     for blob in git.cat(id):
108         conn.write(struct.pack('!I', len(blob)))
109         conn.write(blob)
110     conn.write('\0\0\0\0')
111     conn.ok()
112
113
114 optspec = """
115 bup server
116 """
117 o = options.Options('bup server', optspec)
118 (opt, flags, extra) = o.parse(sys.argv[1:])
119
120 if extra:
121     log('bup server: no arguments expected\n')
122     o.usage()
123
124 log('bup server: reading from stdin.\n')
125
126 commands = {
127     'init-dir': init_dir,
128     'set-dir': set_dir,
129     'list-indexes': list_indexes,
130     'send-index': send_index,
131     'receive-objects': receive_objects,
132     'read-ref': read_ref,
133     'update-ref': update_ref,
134     'cat': cat,
135 }
136
137 # FIXME: this protocol is totally lame and not at all future-proof.
138 # (Especially since we abort completely as soon as *anything* bad happens)
139 conn = Conn(sys.stdin, sys.stdout)
140 lr = linereader(conn)
141 for _line in lr:
142     line = _line.strip()
143     if not line:
144         continue
145     log('bup server: command: %r\n' % line)
146     words = line.split(' ', 1)
147     cmd = words[0]
148     rest = len(words)>1 and words[1] or ''
149     if cmd == 'quit':
150         break
151     else:
152         cmd = commands.get(cmd)
153         if cmd:
154             cmd(conn, rest)
155         else:
156             raise Exception('unknown server command: %r\n' % line)
157
158 log('bup server: done\n')