Mercurial > hg
comparison hgext/zeroconf/__init__.py @ 7071:643c751e60b2
zeroconf: initial implementation
This is a basic, hopefully portable, zeroconf extension.
Enabling it will allow hg paths/pull/push/clone/etc. to automatically
discover services advertised as "_hg".
And naturally, running hg serve will advertise itself as a "_hg"
service as well as a "_http" service for use by browsers.
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 08 Oct 2008 19:58:35 -0500 |
parents | |
children | 62c71741ae7d |
comparison
equal
deleted
inserted
replaced
7070:2627ef59195d | 7071:643c751e60b2 |
---|---|
1 # zeroconf.py - zeroconf support for Mercurial | |
2 # | |
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | |
4 # | |
5 # This software may be used and distributed according to the terms of | |
6 # the GNU General Public License (version 2), incorporated herein by | |
7 # reference. | |
8 | |
9 import Zeroconf, socket, time, os | |
10 from mercurial import ui | |
11 from mercurial.hgweb import hgweb_mod | |
12 from mercurial.hgweb import hgwebdir_mod | |
13 | |
14 # publish | |
15 | |
16 server = None | |
17 localip = None | |
18 | |
19 def getip(): | |
20 # finds external-facing interface without sending any packets (Linux) | |
21 try: | |
22 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
23 s.connect(('1.0.0.1', 0)) | |
24 ip = s.getsockname()[0] | |
25 return ip | |
26 except: | |
27 pass | |
28 | |
29 # Generic method, sometimes gives useless results | |
30 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0] | |
31 if not dumbip.startswith('127.'): | |
32 return dumbip | |
33 | |
34 # works elsewhere, but actually sends a packet | |
35 try: | |
36 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
37 s.connect(('1.0.0.1', 1)) | |
38 ip = s.getsockname()[0] | |
39 return ip | |
40 except: | |
41 pass | |
42 | |
43 return dumbip | |
44 | |
45 def publish(name, desc, path, port): | |
46 global server, localip | |
47 if not server: | |
48 server = Zeroconf.Zeroconf() | |
49 ip = getip() | |
50 localip = socket.inet_aton(ip) | |
51 | |
52 host = socket.gethostname() + ".local" | |
53 | |
54 # advertise to browsers | |
55 svc = Zeroconf.ServiceInfo('_http._tcp.local.', | |
56 name + '._http._tcp.local.', | |
57 server = host, | |
58 port = port, | |
59 properties = {'description': desc, | |
60 'path': "/" + path}, | |
61 address = localip, weight = 0, priority = 0) | |
62 server.registerService(svc) | |
63 | |
64 # advertise to Mercurial clients | |
65 svc = Zeroconf.ServiceInfo('_hg._tcp.local.', | |
66 name + '._hg._tcp.local.', | |
67 port = port, | |
68 properties = {'description': desc, | |
69 'path': "/" + path}, | |
70 address = localip, weight = 0, priority = 0) | |
71 server.registerService(svc) | |
72 | |
73 class hgwebzc(hgweb_mod.hgweb): | |
74 def __init__(self, repo, name=None): | |
75 super(hgwebzc, self).__init__(repo, name) | |
76 name = self.reponame or os.path.basename(repo.root) | |
77 desc = self.repo.ui.config("web", "description", name) | |
78 publish(name, desc, name, int(repo.ui.config("web", "port", 8000))) | |
79 | |
80 class hgwebdirzc(hgwebdir_mod.hgwebdir): | |
81 def run(self): | |
82 print os.environ | |
83 for r, p in self.repos: | |
84 u = ui.ui(parentui=self.parentui) | |
85 u.readconfig(os.path.join(path, '.hg', 'hgrc')) | |
86 n = os.path.basename(r) | |
87 desc = u.config("web", "description", n) | |
88 publish(n, "hgweb", p, int(repo.ui.config("web", "port", 8000))) | |
89 return super(hgwebdirzc, self).run() | |
90 | |
91 # listen | |
92 | |
93 class listener(object): | |
94 def __init__(self): | |
95 self.found = {} | |
96 def removeService(self, server, type, name): | |
97 if repr(name) in self.found: | |
98 del self.found[repr(name)] | |
99 def addService(self, server, type, name): | |
100 self.found[repr(name)] = server.getServiceInfo(type, name) | |
101 | |
102 def getzcpaths(): | |
103 server = Zeroconf.Zeroconf() | |
104 l = listener() | |
105 browser = Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l) | |
106 time.sleep(1) | |
107 server.close() | |
108 for v in l.found.values(): | |
109 n = v.name[:v.name.index('.')] | |
110 n.replace(" ", "-") | |
111 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port, | |
112 v.properties.get("path", "/")) | |
113 yield "zc-" + n, u | |
114 | |
115 def config(self, section, key, default=None, untrusted=False): | |
116 if section == "paths" and key.startswith("zc-"): | |
117 for n, p in getzcpaths(): | |
118 if n == key: | |
119 return p | |
120 return oldconfig(self, section, key, default, untrusted) | |
121 | |
122 def configitems(self, section): | |
123 r = oldconfigitems(self, section, untrusted=False) | |
124 if section == "paths": | |
125 r += getzcpaths() | |
126 return r | |
127 | |
128 oldconfig = ui.ui.config | |
129 oldconfigitems = ui.ui.configitems | |
130 ui.ui.config = config | |
131 ui.ui.configitems = configitems | |
132 hgweb_mod.hgweb = hgwebzc | |
133 hgwebdir_mod.hgwebdir = hgwebdirzc |