##// END OF EJS Templates
sshrepo: move mkstemp() out of the try block, we don't use the exception...
Benoit Boissinot -
r9742:0c84afa1 default
parent child Browse files
Show More
@@ -1,227 +1,223 b''
1 # sshserver.py - ssh protocol server support for mercurial
1 # sshserver.py - ssh protocol server support for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 from i18n import _
9 from i18n import _
10 from node import bin, hex
10 from node import bin, hex
11 import streamclone, util, hook
11 import streamclone, util, hook
12 import os, sys, tempfile, urllib, copy
12 import os, sys, tempfile, urllib, copy
13
13
14 class sshserver(object):
14 class sshserver(object):
15
15
16 caps = 'unbundle lookup changegroupsubset branchmap'.split()
16 caps = 'unbundle lookup changegroupsubset branchmap'.split()
17
17
18 def __init__(self, ui, repo):
18 def __init__(self, ui, repo):
19 self.ui = ui
19 self.ui = ui
20 self.repo = repo
20 self.repo = repo
21 self.lock = None
21 self.lock = None
22 self.fin = sys.stdin
22 self.fin = sys.stdin
23 self.fout = sys.stdout
23 self.fout = sys.stdout
24
24
25 hook.redirect(True)
25 hook.redirect(True)
26 sys.stdout = sys.stderr
26 sys.stdout = sys.stderr
27
27
28 # Prevent insertion/deletion of CRs
28 # Prevent insertion/deletion of CRs
29 util.set_binary(self.fin)
29 util.set_binary(self.fin)
30 util.set_binary(self.fout)
30 util.set_binary(self.fout)
31
31
32 def getarg(self):
32 def getarg(self):
33 argline = self.fin.readline()[:-1]
33 argline = self.fin.readline()[:-1]
34 arg, l = argline.split()
34 arg, l = argline.split()
35 val = self.fin.read(int(l))
35 val = self.fin.read(int(l))
36 return arg, val
36 return arg, val
37
37
38 def respond(self, v):
38 def respond(self, v):
39 self.fout.write("%d\n" % len(v))
39 self.fout.write("%d\n" % len(v))
40 self.fout.write(v)
40 self.fout.write(v)
41 self.fout.flush()
41 self.fout.flush()
42
42
43 def serve_forever(self):
43 def serve_forever(self):
44 try:
44 try:
45 while self.serve_one(): pass
45 while self.serve_one(): pass
46 finally:
46 finally:
47 if self.lock is not None:
47 if self.lock is not None:
48 self.lock.release()
48 self.lock.release()
49 sys.exit(0)
49 sys.exit(0)
50
50
51 def serve_one(self):
51 def serve_one(self):
52 cmd = self.fin.readline()[:-1]
52 cmd = self.fin.readline()[:-1]
53 if cmd:
53 if cmd:
54 impl = getattr(self, 'do_' + cmd, None)
54 impl = getattr(self, 'do_' + cmd, None)
55 if impl: impl()
55 if impl: impl()
56 else: self.respond("")
56 else: self.respond("")
57 return cmd != ''
57 return cmd != ''
58
58
59 def do_lookup(self):
59 def do_lookup(self):
60 arg, key = self.getarg()
60 arg, key = self.getarg()
61 assert arg == 'key'
61 assert arg == 'key'
62 try:
62 try:
63 r = hex(self.repo.lookup(key))
63 r = hex(self.repo.lookup(key))
64 success = 1
64 success = 1
65 except Exception, inst:
65 except Exception, inst:
66 r = str(inst)
66 r = str(inst)
67 success = 0
67 success = 0
68 self.respond("%s %s\n" % (success, r))
68 self.respond("%s %s\n" % (success, r))
69
69
70 def do_branchmap(self):
70 def do_branchmap(self):
71 branchmap = self.repo.branchmap()
71 branchmap = self.repo.branchmap()
72 heads = []
72 heads = []
73 for branch, nodes in branchmap.iteritems():
73 for branch, nodes in branchmap.iteritems():
74 branchname = urllib.quote(branch)
74 branchname = urllib.quote(branch)
75 branchnodes = [hex(node) for node in nodes]
75 branchnodes = [hex(node) for node in nodes]
76 heads.append('%s %s' % (branchname, ' '.join(branchnodes)))
76 heads.append('%s %s' % (branchname, ' '.join(branchnodes)))
77 self.respond('\n'.join(heads))
77 self.respond('\n'.join(heads))
78
78
79 def do_heads(self):
79 def do_heads(self):
80 h = self.repo.heads()
80 h = self.repo.heads()
81 self.respond(" ".join(map(hex, h)) + "\n")
81 self.respond(" ".join(map(hex, h)) + "\n")
82
82
83 def do_hello(self):
83 def do_hello(self):
84 '''the hello command returns a set of lines describing various
84 '''the hello command returns a set of lines describing various
85 interesting things about the server, in an RFC822-like format.
85 interesting things about the server, in an RFC822-like format.
86 Currently the only one defined is "capabilities", which
86 Currently the only one defined is "capabilities", which
87 consists of a line in the form:
87 consists of a line in the form:
88
88
89 capabilities: space separated list of tokens
89 capabilities: space separated list of tokens
90 '''
90 '''
91 caps = copy.copy(self.caps)
91 caps = copy.copy(self.caps)
92 if self.ui.configbool('server', 'uncompressed'):
92 if self.ui.configbool('server', 'uncompressed'):
93 caps.append('stream=%d' % self.repo.changelog.version)
93 caps.append('stream=%d' % self.repo.changelog.version)
94 self.respond("capabilities: %s\n" % (' '.join(caps),))
94 self.respond("capabilities: %s\n" % (' '.join(caps),))
95
95
96 def do_lock(self):
96 def do_lock(self):
97 '''DEPRECATED - allowing remote client to lock repo is not safe'''
97 '''DEPRECATED - allowing remote client to lock repo is not safe'''
98
98
99 self.lock = self.repo.lock()
99 self.lock = self.repo.lock()
100 self.respond("")
100 self.respond("")
101
101
102 def do_unlock(self):
102 def do_unlock(self):
103 '''DEPRECATED'''
103 '''DEPRECATED'''
104
104
105 if self.lock:
105 if self.lock:
106 self.lock.release()
106 self.lock.release()
107 self.lock = None
107 self.lock = None
108 self.respond("")
108 self.respond("")
109
109
110 def do_branches(self):
110 def do_branches(self):
111 arg, nodes = self.getarg()
111 arg, nodes = self.getarg()
112 nodes = map(bin, nodes.split(" "))
112 nodes = map(bin, nodes.split(" "))
113 r = []
113 r = []
114 for b in self.repo.branches(nodes):
114 for b in self.repo.branches(nodes):
115 r.append(" ".join(map(hex, b)) + "\n")
115 r.append(" ".join(map(hex, b)) + "\n")
116 self.respond("".join(r))
116 self.respond("".join(r))
117
117
118 def do_between(self):
118 def do_between(self):
119 arg, pairs = self.getarg()
119 arg, pairs = self.getarg()
120 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
120 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
121 r = []
121 r = []
122 for b in self.repo.between(pairs):
122 for b in self.repo.between(pairs):
123 r.append(" ".join(map(hex, b)) + "\n")
123 r.append(" ".join(map(hex, b)) + "\n")
124 self.respond("".join(r))
124 self.respond("".join(r))
125
125
126 def do_changegroup(self):
126 def do_changegroup(self):
127 nodes = []
127 nodes = []
128 arg, roots = self.getarg()
128 arg, roots = self.getarg()
129 nodes = map(bin, roots.split(" "))
129 nodes = map(bin, roots.split(" "))
130
130
131 cg = self.repo.changegroup(nodes, 'serve')
131 cg = self.repo.changegroup(nodes, 'serve')
132 while True:
132 while True:
133 d = cg.read(4096)
133 d = cg.read(4096)
134 if not d:
134 if not d:
135 break
135 break
136 self.fout.write(d)
136 self.fout.write(d)
137
137
138 self.fout.flush()
138 self.fout.flush()
139
139
140 def do_changegroupsubset(self):
140 def do_changegroupsubset(self):
141 argmap = dict([self.getarg(), self.getarg()])
141 argmap = dict([self.getarg(), self.getarg()])
142 bases = [bin(n) for n in argmap['bases'].split(' ')]
142 bases = [bin(n) for n in argmap['bases'].split(' ')]
143 heads = [bin(n) for n in argmap['heads'].split(' ')]
143 heads = [bin(n) for n in argmap['heads'].split(' ')]
144
144
145 cg = self.repo.changegroupsubset(bases, heads, 'serve')
145 cg = self.repo.changegroupsubset(bases, heads, 'serve')
146 while True:
146 while True:
147 d = cg.read(4096)
147 d = cg.read(4096)
148 if not d:
148 if not d:
149 break
149 break
150 self.fout.write(d)
150 self.fout.write(d)
151
151
152 self.fout.flush()
152 self.fout.flush()
153
153
154 def do_addchangegroup(self):
154 def do_addchangegroup(self):
155 '''DEPRECATED'''
155 '''DEPRECATED'''
156
156
157 if not self.lock:
157 if not self.lock:
158 self.respond("not locked")
158 self.respond("not locked")
159 return
159 return
160
160
161 self.respond("")
161 self.respond("")
162 r = self.repo.addchangegroup(self.fin, 'serve', self.client_url())
162 r = self.repo.addchangegroup(self.fin, 'serve', self.client_url())
163 self.respond(str(r))
163 self.respond(str(r))
164
164
165 def client_url(self):
165 def client_url(self):
166 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
166 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
167 return 'remote:ssh:' + client
167 return 'remote:ssh:' + client
168
168
169 def do_unbundle(self):
169 def do_unbundle(self):
170 their_heads = self.getarg()[1].split()
170 their_heads = self.getarg()[1].split()
171
171
172 def check_heads():
172 def check_heads():
173 heads = map(hex, self.repo.heads())
173 heads = map(hex, self.repo.heads())
174 return their_heads == [hex('force')] or their_heads == heads
174 return their_heads == [hex('force')] or their_heads == heads
175
175
176 # fail early if possible
176 # fail early if possible
177 if not check_heads():
177 if not check_heads():
178 self.respond(_('unsynced changes'))
178 self.respond(_('unsynced changes'))
179 return
179 return
180
180
181 self.respond('')
181 self.respond('')
182
182
183 # write bundle data to temporary file because it can be big
183 # write bundle data to temporary file because it can be big
184 tempname = fp = None
185 try:
186 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
184 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
187 fp = os.fdopen(fd, 'wb+')
185 fp = os.fdopen(fd, 'wb+')
188
186 try:
189 count = int(self.fin.readline())
187 count = int(self.fin.readline())
190 while count:
188 while count:
191 fp.write(self.fin.read(count))
189 fp.write(self.fin.read(count))
192 count = int(self.fin.readline())
190 count = int(self.fin.readline())
193
191
194 was_locked = self.lock is not None
192 was_locked = self.lock is not None
195 if not was_locked:
193 if not was_locked:
196 self.lock = self.repo.lock()
194 self.lock = self.repo.lock()
197 try:
195 try:
198 if not check_heads():
196 if not check_heads():
199 # someone else committed/pushed/unbundled while we
197 # someone else committed/pushed/unbundled while we
200 # were transferring data
198 # were transferring data
201 self.respond(_('unsynced changes'))
199 self.respond(_('unsynced changes'))
202 return
200 return
203 self.respond('')
201 self.respond('')
204
202
205 # push can proceed
203 # push can proceed
206
204
207 fp.seek(0)
205 fp.seek(0)
208 r = self.repo.addchangegroup(fp, 'serve', self.client_url())
206 r = self.repo.addchangegroup(fp, 'serve', self.client_url())
209 self.respond(str(r))
207 self.respond(str(r))
210 finally:
208 finally:
211 if not was_locked:
209 if not was_locked:
212 self.lock.release()
210 self.lock.release()
213 self.lock = None
211 self.lock = None
214 finally:
212 finally:
215 if fp is not None:
216 fp.close()
213 fp.close()
217 if tempname is not None:
218 os.unlink(tempname)
214 os.unlink(tempname)
219
215
220 def do_stream_out(self):
216 def do_stream_out(self):
221 try:
217 try:
222 for chunk in streamclone.stream_out(self.repo):
218 for chunk in streamclone.stream_out(self.repo):
223 self.fout.write(chunk)
219 self.fout.write(chunk)
224 self.fout.flush()
220 self.fout.flush()
225 except streamclone.StreamException, inst:
221 except streamclone.StreamException, inst:
226 self.fout.write(str(inst))
222 self.fout.write(str(inst))
227 self.fout.flush()
223 self.fout.flush()
General Comments 0
You need to be logged in to leave comments. Login now