__init__.py
161 lines
| 5.0 KiB
| text/x-python
|
PythonLexer
Matt Mackall
|
r7071 | # zeroconf.py - zeroconf support for Mercurial | ||
# | ||||
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | ||||
# | ||||
# This software may be used and distributed according to the terms of | ||||
# the GNU General Public License (version 2), incorporated herein by | ||||
# reference. | ||||
David Soria Parra
|
r7606 | '''zeroconf support for mercurial repositories | ||
Martin Geisler
|
r8003 | Zeroconf enabled repositories will be announced in a network without | ||
the need to configure a server or a service. They can be discovered | ||||
without knowing their actual IP address. | ||||
David Soria Parra
|
r7606 | |||
Martin Geisler
|
r8003 | To use the zeroconf extension add the following entry to your hgrc | ||
file: | ||||
David Soria Parra
|
r7606 | |||
[extensions] | ||||
hgext.zeroconf = | ||||
Martin Geisler
|
r8003 | To allow other people to discover your repository using run "hg serve" | ||
in your repository. | ||||
David Soria Parra
|
r7606 | |||
$ cd test | ||||
$ hg serve | ||||
You can discover zeroconf enabled repositories by running "hg paths". | ||||
$ hg paths | ||||
zc-test = http://example.com:8000/test | ||||
''' | ||||
Matt Mackall
|
r7071 | import Zeroconf, socket, time, os | ||
from mercurial import ui | ||||
Matt Mackall
|
r7216 | from mercurial import extensions | ||
Matt Mackall
|
r7071 | from mercurial.hgweb import hgweb_mod | ||
from mercurial.hgweb import hgwebdir_mod | ||||
# publish | ||||
server = None | ||||
localip = None | ||||
def getip(): | ||||
# finds external-facing interface without sending any packets (Linux) | ||||
try: | ||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||||
s.connect(('1.0.0.1', 0)) | ||||
ip = s.getsockname()[0] | ||||
return ip | ||||
except: | ||||
pass | ||||
# Generic method, sometimes gives useless results | ||||
dumbip = socket.gethostbyaddr(socket.gethostname())[2][0] | ||||
Alexander Solovyov
|
r7777 | if not dumbip.startswith('127.') and ':' not in dumbip: | ||
Matt Mackall
|
r7071 | return dumbip | ||
# works elsewhere, but actually sends a packet | ||||
try: | ||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||||
s.connect(('1.0.0.1', 1)) | ||||
ip = s.getsockname()[0] | ||||
return ip | ||||
except: | ||||
pass | ||||
return dumbip | ||||
def publish(name, desc, path, port): | ||||
global server, localip | ||||
if not server: | ||||
Augie Fackler
|
r7295 | try: | ||
server = Zeroconf.Zeroconf() | ||||
except socket.gaierror: | ||||
# if we have no internet connection, this can happen. | ||||
return | ||||
Matt Mackall
|
r7071 | ip = getip() | ||
localip = socket.inet_aton(ip) | ||||
Alexander Solovyov
|
r7845 | hostname = socket.gethostname().split('.')[0] | ||
host = hostname + ".local" | ||||
name = "%s-%s" % (hostname, name) | ||||
Matt Mackall
|
r7071 | |||
# advertise to browsers | ||||
svc = Zeroconf.ServiceInfo('_http._tcp.local.', | ||||
name + '._http._tcp.local.', | ||||
server = host, | ||||
port = port, | ||||
properties = {'description': desc, | ||||
'path': "/" + path}, | ||||
address = localip, weight = 0, priority = 0) | ||||
server.registerService(svc) | ||||
# advertise to Mercurial clients | ||||
svc = Zeroconf.ServiceInfo('_hg._tcp.local.', | ||||
name + '._hg._tcp.local.', | ||||
Matt Mackall
|
r7088 | server = host, | ||
Matt Mackall
|
r7071 | port = port, | ||
properties = {'description': desc, | ||||
'path': "/" + path}, | ||||
address = localip, weight = 0, priority = 0) | ||||
server.registerService(svc) | ||||
class hgwebzc(hgweb_mod.hgweb): | ||||
def __init__(self, repo, name=None): | ||||
super(hgwebzc, self).__init__(repo, name) | ||||
name = self.reponame or os.path.basename(repo.root) | ||||
desc = self.repo.ui.config("web", "description", name) | ||||
publish(name, desc, name, int(repo.ui.config("web", "port", 8000))) | ||||
class hgwebdirzc(hgwebdir_mod.hgwebdir): | ||||
def run(self): | ||||
for r, p in self.repos: | ||||
u = ui.ui(parentui=self.parentui) | ||||
Benoit Boissinot
|
r7282 | u.readconfig(os.path.join(p, '.hg', 'hgrc')) | ||
Matt Mackall
|
r7071 | n = os.path.basename(r) | ||
Benoit Boissinot
|
r7282 | publish(n, "hgweb", p, int(u.config("web", "port", 8000))) | ||
Matt Mackall
|
r7071 | return super(hgwebdirzc, self).run() | ||
# listen | ||||
class listener(object): | ||||
def __init__(self): | ||||
self.found = {} | ||||
def removeService(self, server, type, name): | ||||
if repr(name) in self.found: | ||||
del self.found[repr(name)] | ||||
def addService(self, server, type, name): | ||||
self.found[repr(name)] = server.getServiceInfo(type, name) | ||||
def getzcpaths(): | ||||
server = Zeroconf.Zeroconf() | ||||
l = listener() | ||||
Peter Arrenbrecht
|
r7874 | Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l) | ||
Matt Mackall
|
r7071 | time.sleep(1) | ||
server.close() | ||||
for v in l.found.values(): | ||||
n = v.name[:v.name.index('.')] | ||||
n.replace(" ", "-") | ||||
u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port, | ||||
v.properties.get("path", "/")) | ||||
yield "zc-" + n, u | ||||
Matt Mackall
|
r7216 | def config(orig, self, section, key, default=None, untrusted=False): | ||
Matt Mackall
|
r7071 | if section == "paths" and key.startswith("zc-"): | ||
for n, p in getzcpaths(): | ||||
if n == key: | ||||
return p | ||||
Matt Mackall
|
r7216 | return orig(self, section, key, default, untrusted) | ||
Matt Mackall
|
r7071 | |||
Matt Mackall
|
r7238 | def configitems(orig, self, section, untrusted=False): | ||
r = orig(self, section, untrusted) | ||||
Matt Mackall
|
r7071 | if section == "paths": | ||
r += getzcpaths() | ||||
return r | ||||
Matt Mackall
|
r7216 | extensions.wrapfunction(ui.ui, 'config', config) | ||
extensions.wrapfunction(ui.ui, 'configitems', configitems) | ||||
Matt Mackall
|
r7071 | hgweb_mod.hgweb = hgwebzc | ||
hgwebdir_mod.hgwebdir = hgwebdirzc | ||||