##// END OF EJS Templates
zeroconf: notify the Zeroconf threads when hg exits...
Nicolas Dumazet -
r14104:23fc62e0 stable
parent child Browse files
Show More
@@ -1,173 +1,185
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
8 8 '''discover and advertise repositories on the local network
9 9
10 10 Zeroconf-enabled repositories will be announced in a network without
11 11 the need to configure a server or a service. They can be discovered
12 12 without knowing their actual IP address.
13 13
14 14 To allow other people to discover your repository using run
15 15 :hg:`serve` in your repository::
16 16
17 17 $ cd test
18 18 $ hg serve
19 19
20 20 You can discover Zeroconf-enabled repositories by running
21 21 :hg:`paths`::
22 22
23 23 $ hg paths
24 24 zc-test = http://example.com:8000/test
25 25 '''
26 26
27 27 import socket, time, os
28 28
29 29 import Zeroconf
30 from mercurial import ui, hg, encoding, util
30 from mercurial import ui, hg, encoding, util, dispatch
31 31 from mercurial import extensions
32 32 from mercurial.hgweb import hgweb_mod
33 33 from mercurial.hgweb import hgwebdir_mod
34 34
35 35 # publish
36 36
37 37 server = None
38 38 localip = None
39 39
40 40 def getip():
41 41 # finds external-facing interface without sending any packets (Linux)
42 42 try:
43 43 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
44 44 s.connect(('1.0.0.1', 0))
45 45 ip = s.getsockname()[0]
46 46 return ip
47 47 except:
48 48 pass
49 49
50 50 # Generic method, sometimes gives useless results
51 51 try:
52 52 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
53 53 if not dumbip.startswith('127.') and ':' not in dumbip:
54 54 return dumbip
55 55 except (socket.gaierror, socket.herror):
56 56 dumbip = '127.0.0.1'
57 57
58 58 # works elsewhere, but actually sends a packet
59 59 try:
60 60 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
61 61 s.connect(('1.0.0.1', 1))
62 62 ip = s.getsockname()[0]
63 63 return ip
64 64 except:
65 65 pass
66 66
67 67 return dumbip
68 68
69 69 def publish(name, desc, path, port):
70 70 global server, localip
71 71 if not server:
72 72 ip = getip()
73 73 if ip.startswith('127.'):
74 74 # if we have no internet connection, this can happen.
75 75 return
76 76 localip = socket.inet_aton(ip)
77 77 server = Zeroconf.Zeroconf(ip)
78 78
79 79 hostname = socket.gethostname().split('.')[0]
80 80 host = hostname + ".local"
81 81 name = "%s-%s" % (hostname, name)
82 82
83 83 # advertise to browsers
84 84 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
85 85 name + '._http._tcp.local.',
86 86 server = host,
87 87 port = port,
88 88 properties = {'description': desc,
89 89 'path': "/" + path},
90 90 address = localip, weight = 0, priority = 0)
91 91 server.registerService(svc)
92 92
93 93 # advertise to Mercurial clients
94 94 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
95 95 name + '._hg._tcp.local.',
96 96 server = host,
97 97 port = port,
98 98 properties = {'description': desc,
99 99 'path': "/" + path},
100 100 address = localip, weight = 0, priority = 0)
101 101 server.registerService(svc)
102 102
103 103 class hgwebzc(hgweb_mod.hgweb):
104 104 def __init__(self, repo, name=None, baseui=None):
105 105 super(hgwebzc, self).__init__(repo, name=name, baseui=baseui)
106 106 name = self.reponame or os.path.basename(self.repo.root)
107 107 path = self.repo.ui.config("web", "prefix", "").strip('/')
108 108 desc = self.repo.ui.config("web", "description", name)
109 109 publish(name, desc, path,
110 110 util.getport(self.repo.ui.config("web", "port", 8000)))
111 111
112 112 class hgwebdirzc(hgwebdir_mod.hgwebdir):
113 113 def __init__(self, conf, baseui=None):
114 114 super(hgwebdirzc, self).__init__(conf, baseui=baseui)
115 115 prefix = self.ui.config("web", "prefix", "").strip('/') + '/'
116 116 for repo, path in self.repos:
117 117 u = self.ui.copy()
118 118 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
119 119 name = os.path.basename(repo)
120 120 path = (prefix + repo).strip('/')
121 121 desc = u.config('web', 'description', name)
122 122 publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
123 123
124 124 # listen
125 125
126 126 class listener(object):
127 127 def __init__(self):
128 128 self.found = {}
129 129 def removeService(self, server, type, name):
130 130 if repr(name) in self.found:
131 131 del self.found[repr(name)]
132 132 def addService(self, server, type, name):
133 133 self.found[repr(name)] = server.getServiceInfo(type, name)
134 134
135 135 def getzcpaths():
136 136 ip = getip()
137 137 if ip.startswith('127.'):
138 138 return
139 139 server = Zeroconf.Zeroconf(ip)
140 140 l = listener()
141 141 Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
142 142 time.sleep(1)
143 143 server.close()
144 144 for value in l.found.values():
145 145 name = value.name[:value.name.index('.')]
146 146 url = "http://%s:%s%s" % (socket.inet_ntoa(value.address), value.port,
147 147 value.properties.get("path", "/"))
148 148 yield "zc-" + name, url
149 149
150 150 def config(orig, self, section, key, default=None, untrusted=False):
151 151 if section == "paths" and key.startswith("zc-"):
152 152 for name, path in getzcpaths():
153 153 if name == key:
154 154 return path
155 155 return orig(self, section, key, default, untrusted)
156 156
157 157 def configitems(orig, self, section, untrusted=False):
158 158 repos = orig(self, section, untrusted)
159 159 if section == "paths":
160 160 repos += getzcpaths()
161 161 return repos
162 162
163 163 def defaultdest(orig, source):
164 164 for name, path in getzcpaths():
165 165 if path == source:
166 166 return name.encode(encoding.encoding)
167 167 return orig(source)
168 168
169 def cleanupafterdispatch(orig, ui, options, cmd, cmdfunc):
170 try:
171 return orig(ui, options, cmd, cmdfunc)
172 finally:
173 # we need to call close() on the server to notify() the various
174 # threading Conditions and allow the background threads to exit
175 global server
176 if server:
177 server.close()
178
179 extensions.wrapfunction(dispatch, '_runcommand', cleanupafterdispatch)
180
169 181 extensions.wrapfunction(ui.ui, 'config', config)
170 182 extensions.wrapfunction(ui.ui, 'configitems', configitems)
171 183 extensions.wrapfunction(hg, 'defaultdest', defaultdest)
172 184 hgweb_mod.hgweb = hgwebzc
173 185 hgwebdir_mod.hgwebdir = hgwebdirzc
General Comments 0
You need to be logged in to leave comments. Login now