##// END OF EJS Templates
zeroconf: import ui as uimod per test-check-module-imports
Augie Fackler -
r28308:4de74eda default
parent child Browse files
Show More
@@ -1,213 +1,213
1 1 # zeroconf.py - zeroconf support for Mercurial
2 2 #
3 3 # Copyright 2005-2007 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 '''discover and advertise repositories on the local network
8 8
9 9 Zeroconf-enabled repositories will be announced in a network without
10 10 the need to configure a server or a service. They can be discovered
11 11 without knowing their actual IP address.
12 12
13 13 To allow other people to discover your repository using run
14 14 :hg:`serve` in your repository::
15 15
16 16 $ cd test
17 17 $ hg serve
18 18
19 19 You can discover Zeroconf-enabled repositories by running
20 20 :hg:`paths`::
21 21
22 22 $ hg paths
23 23 zc-test = http://example.com:8000/test
24 24 '''
25 25 from __future__ import absolute_import
26 26
27 27 import os
28 28 import socket
29 29 import time
30 30
31 31 from . import Zeroconf
32 32 from mercurial import (
33 33 dispatch,
34 34 encoding,
35 35 extensions,
36 36 hg,
37 ui,
37 ui as uimod,
38 38 )
39 39 from mercurial.hgweb import (
40 40 server as servermod
41 41 )
42 42
43 43 # Note for extension authors: ONLY specify testedwith = 'internal' for
44 44 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
45 45 # be specifying the version(s) of Mercurial they are tested with, or
46 46 # leave the attribute unspecified.
47 47 testedwith = 'internal'
48 48
49 49 # publish
50 50
51 51 server = None
52 52 localip = None
53 53
54 54 def getip():
55 55 # finds external-facing interface without sending any packets (Linux)
56 56 try:
57 57 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
58 58 s.connect(('1.0.0.1', 0))
59 59 ip = s.getsockname()[0]
60 60 return ip
61 61 except socket.error:
62 62 pass
63 63
64 64 # Generic method, sometimes gives useless results
65 65 try:
66 66 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
67 67 if not dumbip.startswith('127.') and ':' not in dumbip:
68 68 return dumbip
69 69 except (socket.gaierror, socket.herror):
70 70 dumbip = '127.0.0.1'
71 71
72 72 # works elsewhere, but actually sends a packet
73 73 try:
74 74 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
75 75 s.connect(('1.0.0.1', 1))
76 76 ip = s.getsockname()[0]
77 77 return ip
78 78 except socket.error:
79 79 pass
80 80
81 81 return dumbip
82 82
83 83 def publish(name, desc, path, port):
84 84 global server, localip
85 85 if not server:
86 86 ip = getip()
87 87 if ip.startswith('127.'):
88 88 # if we have no internet connection, this can happen.
89 89 return
90 90 localip = socket.inet_aton(ip)
91 91 server = Zeroconf.Zeroconf(ip)
92 92
93 93 hostname = socket.gethostname().split('.')[0]
94 94 host = hostname + ".local"
95 95 name = "%s-%s" % (hostname, name)
96 96
97 97 # advertise to browsers
98 98 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
99 99 name + '._http._tcp.local.',
100 100 server = host,
101 101 port = port,
102 102 properties = {'description': desc,
103 103 'path': "/" + path},
104 104 address = localip, weight = 0, priority = 0)
105 105 server.registerService(svc)
106 106
107 107 # advertise to Mercurial clients
108 108 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
109 109 name + '._hg._tcp.local.',
110 110 server = host,
111 111 port = port,
112 112 properties = {'description': desc,
113 113 'path': "/" + path},
114 114 address = localip, weight = 0, priority = 0)
115 115 server.registerService(svc)
116 116
117 117 def zc_create_server(create_server, ui, app):
118 118 httpd = create_server(ui, app)
119 119 port = httpd.port
120 120
121 121 try:
122 122 repos = app.repos
123 123 except AttributeError:
124 124 # single repo
125 125 with app._obtainrepo() as repo:
126 126 name = app.reponame or os.path.basename(repo.root)
127 127 path = repo.ui.config("web", "prefix", "").strip('/')
128 128 desc = repo.ui.config("web", "description", name)
129 129 publish(name, desc, path, port)
130 130 else:
131 131 # webdir
132 132 prefix = app.ui.config("web", "prefix", "").strip('/') + '/'
133 133 for repo, path in repos:
134 134 u = app.ui.copy()
135 135 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
136 136 name = os.path.basename(repo)
137 137 path = (prefix + repo).strip('/')
138 138 desc = u.config('web', 'description', name)
139 139 publish(name, desc, path, port)
140 140 return httpd
141 141
142 142 # listen
143 143
144 144 class listener(object):
145 145 def __init__(self):
146 146 self.found = {}
147 147 def removeService(self, server, type, name):
148 148 if repr(name) in self.found:
149 149 del self.found[repr(name)]
150 150 def addService(self, server, type, name):
151 151 self.found[repr(name)] = server.getServiceInfo(type, name)
152 152
153 153 def getzcpaths():
154 154 ip = getip()
155 155 if ip.startswith('127.'):
156 156 return
157 157 server = Zeroconf.Zeroconf(ip)
158 158 l = listener()
159 159 Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
160 160 time.sleep(1)
161 161 server.close()
162 162 for value in l.found.values():
163 163 name = value.name[:value.name.index('.')]
164 164 url = "http://%s:%s%s" % (socket.inet_ntoa(value.address), value.port,
165 165 value.properties.get("path", "/"))
166 166 yield "zc-" + name, url
167 167
168 168 def config(orig, self, section, key, default=None, untrusted=False):
169 169 if section == "paths" and key.startswith("zc-"):
170 170 for name, path in getzcpaths():
171 171 if name == key:
172 172 return path
173 173 return orig(self, section, key, default, untrusted)
174 174
175 175 def configitems(orig, self, section, *args, **kwargs):
176 176 repos = orig(self, section, *args, **kwargs)
177 177 if section == "paths":
178 178 repos += getzcpaths()
179 179 return repos
180 180
181 181 def configsuboptions(orig, self, section, name, *args, **kwargs):
182 182 opt, sub = orig(self, section, name, *args, **kwargs)
183 183 if section == "paths" and name.startswith("zc-"):
184 184 # We have to find the URL in the zeroconf paths. We can't cons up any
185 185 # suboptions, so we use any that we found in the original config.
186 186 for zcname, zcurl in getzcpaths():
187 187 if zcname == name:
188 188 return zcurl, sub
189 189 return opt, sub
190 190
191 191 def defaultdest(orig, source):
192 192 for name, path in getzcpaths():
193 193 if path == source:
194 194 return name.encode(encoding.encoding)
195 195 return orig(source)
196 196
197 197 def cleanupafterdispatch(orig, ui, options, cmd, cmdfunc):
198 198 try:
199 199 return orig(ui, options, cmd, cmdfunc)
200 200 finally:
201 201 # we need to call close() on the server to notify() the various
202 202 # threading Conditions and allow the background threads to exit
203 203 global server
204 204 if server:
205 205 server.close()
206 206
207 207 extensions.wrapfunction(dispatch, '_runcommand', cleanupafterdispatch)
208 208
209 extensions.wrapfunction(ui.ui, 'config', config)
210 extensions.wrapfunction(ui.ui, 'configitems', configitems)
211 extensions.wrapfunction(ui.ui, 'configsuboptions', configsuboptions)
209 extensions.wrapfunction(uimod.ui, 'config', config)
210 extensions.wrapfunction(uimod.ui, 'configitems', configitems)
211 extensions.wrapfunction(uimod.ui, 'configsuboptions', configsuboptions)
212 212 extensions.wrapfunction(hg, 'defaultdest', defaultdest)
213 213 extensions.wrapfunction(servermod, 'create_server', zc_create_server)
General Comments 0
You need to be logged in to leave comments. Login now