##// END OF EJS Templates
sshpeer: mark _validrepo internal
Matt Mackall -
r19405:44733297 default
parent child Browse files
Show More
@@ -1,242 +1,242 b''
1 1 # sshpeer.py - ssh repository proxy class for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import re
9 9 from i18n import _
10 10 import util, error, wireproto
11 11
12 12 class remotelock(object):
13 13 def __init__(self, repo):
14 14 self.repo = repo
15 15 def release(self):
16 16 self.repo.unlock()
17 17 self.repo = None
18 18 def __del__(self):
19 19 if self.repo:
20 20 self.release()
21 21
22 22 def _serverquote(s):
23 23 '''quote a string for the remote shell ... which we assume is sh'''
24 24 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
25 25 return s
26 26 return "'%s'" % s.replace("'", "'\\''")
27 27
28 28 class sshpeer(wireproto.wirepeer):
29 29 def __init__(self, ui, path, create=False):
30 30 self._url = path
31 31 self.ui = ui
32 32 self.pipeo = self.pipei = self.pipee = None
33 33
34 34 u = util.url(path, parsequery=False, parsefragment=False)
35 35 if u.scheme != 'ssh' or not u.host or u.path is None:
36 36 self._abort(error.RepoError(_("couldn't parse location %s") % path))
37 37
38 38 self.user = u.user
39 39 if u.passwd is not None:
40 40 self._abort(error.RepoError(_("password in URL not supported")))
41 41 self.host = u.host
42 42 self.port = u.port
43 43 self.path = u.path or "."
44 44
45 45 sshcmd = self.ui.config("ui", "ssh", "ssh")
46 46 remotecmd = self.ui.config("ui", "remotecmd", "hg")
47 47
48 48 args = util.sshargs(sshcmd, self.host, self.user, self.port)
49 49
50 50 if create:
51 51 cmd = '%s %s %s' % (sshcmd, args,
52 52 util.shellquote("%s init %s" %
53 53 (_serverquote(remotecmd), _serverquote(self.path))))
54 54 ui.note(_('running %s\n') % cmd)
55 55 res = util.system(cmd)
56 56 if res != 0:
57 57 self._abort(error.RepoError(_("could not create remote repo")))
58 58
59 self.validate_repo(ui, sshcmd, args, remotecmd)
59 self._validaterepo(sshcmd, args, remotecmd)
60 60
61 61 def url(self):
62 62 return self._url
63 63
64 def validate_repo(self, ui, sshcmd, args, remotecmd):
64 def _validaterepo(self, sshcmd, args, remotecmd):
65 65 # cleanup up previous run
66 66 self.cleanup()
67 67
68 68 cmd = '%s %s %s' % (sshcmd, args,
69 69 util.shellquote("%s -R %s serve --stdio" %
70 70 (_serverquote(remotecmd), _serverquote(self.path))))
71 ui.note(_('running %s\n') % cmd)
71 self.ui.note(_('running %s\n') % cmd)
72 72 cmd = util.quotecommand(cmd)
73 73
74 74 # while self.subprocess isn't used, having it allows the subprocess to
75 75 # to clean up correctly later
76 76 self.pipeo, self.pipei, self.pipee, self.subprocess = util.popen4(cmd)
77 77
78 78 # skip any noise generated by remote shell
79 79 self._callstream("hello")
80 80 r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
81 81 lines = ["", "dummy"]
82 82 max_noise = 500
83 83 while lines[-1] and max_noise:
84 84 l = r.readline()
85 85 self.readerr()
86 86 if lines[-1] == "1\n" and l == "\n":
87 87 break
88 88 if l:
89 ui.debug("remote: ", l)
89 self.ui.debug("remote: ", l)
90 90 lines.append(l)
91 91 max_noise -= 1
92 92 else:
93 93 self._abort(error.RepoError(_('no suitable response from '
94 94 'remote hg')))
95 95
96 96 self._caps = set()
97 97 for l in reversed(lines):
98 98 if l.startswith("capabilities:"):
99 99 self._caps.update(l[:-1].split(":")[1].split())
100 100 break
101 101
102 102 def _capabilities(self):
103 103 return self._caps
104 104
105 105 def readerr(self):
106 106 while True:
107 107 size = util.fstat(self.pipee).st_size
108 108 if size == 0:
109 109 break
110 110 s = self.pipee.read(size)
111 111 if not s:
112 112 break
113 113 for l in s.splitlines():
114 114 self.ui.status(_("remote: "), l, '\n')
115 115
116 116 def _abort(self, exception):
117 117 self.cleanup()
118 118 raise exception
119 119
120 120 def cleanup(self):
121 121 if self.pipeo is None:
122 122 return
123 123 self.pipeo.close()
124 124 self.pipei.close()
125 125 try:
126 126 # read the error descriptor until EOF
127 127 for l in self.pipee:
128 128 self.ui.status(_("remote: "), l)
129 129 except (IOError, ValueError):
130 130 pass
131 131 self.pipee.close()
132 132
133 133 __del__ = cleanup
134 134
135 135 def _callstream(self, cmd, **args):
136 136 self.ui.debug("sending %s command\n" % cmd)
137 137 self.pipeo.write("%s\n" % cmd)
138 138 _func, names = wireproto.commands[cmd]
139 139 keys = names.split()
140 140 wireargs = {}
141 141 for k in keys:
142 142 if k == '*':
143 143 wireargs['*'] = args
144 144 break
145 145 else:
146 146 wireargs[k] = args[k]
147 147 del args[k]
148 148 for k, v in sorted(wireargs.iteritems()):
149 149 self.pipeo.write("%s %d\n" % (k, len(v)))
150 150 if isinstance(v, dict):
151 151 for dk, dv in v.iteritems():
152 152 self.pipeo.write("%s %d\n" % (dk, len(dv)))
153 153 self.pipeo.write(dv)
154 154 else:
155 155 self.pipeo.write(v)
156 156 self.pipeo.flush()
157 157
158 158 return self.pipei
159 159
160 160 def _call(self, cmd, **args):
161 161 self._callstream(cmd, **args)
162 162 return self._recv()
163 163
164 164 def _callpush(self, cmd, fp, **args):
165 165 r = self._call(cmd, **args)
166 166 if r:
167 167 return '', r
168 168 while True:
169 169 d = fp.read(4096)
170 170 if not d:
171 171 break
172 172 self._send(d)
173 173 self._send("", flush=True)
174 174 r = self._recv()
175 175 if r:
176 176 return '', r
177 177 return self._recv(), ''
178 178
179 179 def _decompress(self, stream):
180 180 return stream
181 181
182 182 def _recv(self):
183 183 l = self.pipei.readline()
184 184 if l == '\n':
185 185 err = []
186 186 while True:
187 187 line = self.pipee.readline()
188 188 if line == '-\n':
189 189 break
190 190 err.extend([line])
191 191 if len(err) > 0:
192 192 # strip the trailing newline added to the last line server-side
193 193 err[-1] = err[-1][:-1]
194 194 self._abort(error.OutOfBandError(*err))
195 195 self.readerr()
196 196 try:
197 197 l = int(l)
198 198 except ValueError:
199 199 self._abort(error.ResponseError(_("unexpected response:"), l))
200 200 return self.pipei.read(l)
201 201
202 202 def _send(self, data, flush=False):
203 203 self.pipeo.write("%d\n" % len(data))
204 204 if data:
205 205 self.pipeo.write(data)
206 206 if flush:
207 207 self.pipeo.flush()
208 208 self.readerr()
209 209
210 210 def lock(self):
211 211 self._call("lock")
212 212 return remotelock(self)
213 213
214 214 def unlock(self):
215 215 self._call("unlock")
216 216
217 217 def addchangegroup(self, cg, source, url, lock=None):
218 218 '''Send a changegroup to the remote server. Return an integer
219 219 similar to unbundle(). DEPRECATED, since it requires locking the
220 220 remote.'''
221 221 d = self._call("addchangegroup")
222 222 if d:
223 223 self._abort(error.RepoError(_("push refused: %s") % d))
224 224 while True:
225 225 d = cg.read(4096)
226 226 if not d:
227 227 break
228 228 self.pipeo.write(d)
229 229 self.readerr()
230 230
231 231 self.pipeo.flush()
232 232
233 233 self.readerr()
234 234 r = self._recv()
235 235 if not r:
236 236 return 1
237 237 try:
238 238 return int(r)
239 239 except ValueError:
240 240 self._abort(error.ResponseError(_("unexpected response:"), r))
241 241
242 242 instance = sshpeer
General Comments 0
You need to be logged in to leave comments. Login now