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