##// END OF EJS Templates
zeroconf: advertise repositories with hostname
Alexander Solovyov -
r7845:c2cd8d77 default
parent child Browse files
Show More
@@ -1,159 +1,160
1 # zeroconf.py - zeroconf support for Mercurial
1 # zeroconf.py - zeroconf support for Mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of
5 # This software may be used and distributed according to the terms of
6 # the GNU General Public License (version 2), incorporated herein by
6 # the GNU General Public License (version 2), incorporated herein by
7 # reference.
7 # reference.
8
8
9 '''zeroconf support for mercurial repositories
9 '''zeroconf support for mercurial repositories
10
10
11 Zeroconf enabled repositories will be announced in a network without the need
11 Zeroconf enabled repositories will be announced in a network without the need
12 to configure a server or a service. They can be discovered without knowing
12 to configure a server or a service. They can be discovered without knowing
13 their actual IP address.
13 their actual IP address.
14
14
15 To use the zeroconf extension add the following entry to your hgrc file:
15 To use the zeroconf extension add the following entry to your hgrc file:
16
16
17 [extensions]
17 [extensions]
18 hgext.zeroconf =
18 hgext.zeroconf =
19
19
20 To allow other people to discover your repository using run "hg serve" in your
20 To allow other people to discover your repository using run "hg serve" in your
21 repository.
21 repository.
22
22
23 $ cd test
23 $ cd test
24 $ hg serve
24 $ hg serve
25
25
26 You can discover zeroconf enabled repositories by running "hg paths".
26 You can discover zeroconf enabled repositories by running "hg paths".
27
27
28 $ hg paths
28 $ hg paths
29 zc-test = http://example.com:8000/test
29 zc-test = http://example.com:8000/test
30 '''
30 '''
31
31
32 import Zeroconf, socket, time, os
32 import Zeroconf, socket, time, os
33 from mercurial import ui
33 from mercurial import ui
34 from mercurial import extensions
34 from mercurial import extensions
35 from mercurial.hgweb import hgweb_mod
35 from mercurial.hgweb import hgweb_mod
36 from mercurial.hgweb import hgwebdir_mod
36 from mercurial.hgweb import hgwebdir_mod
37
37
38 # publish
38 # publish
39
39
40 server = None
40 server = None
41 localip = None
41 localip = None
42
42
43 def getip():
43 def getip():
44 # finds external-facing interface without sending any packets (Linux)
44 # finds external-facing interface without sending any packets (Linux)
45 try:
45 try:
46 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
46 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
47 s.connect(('1.0.0.1', 0))
47 s.connect(('1.0.0.1', 0))
48 ip = s.getsockname()[0]
48 ip = s.getsockname()[0]
49 return ip
49 return ip
50 except:
50 except:
51 pass
51 pass
52
52
53 # Generic method, sometimes gives useless results
53 # Generic method, sometimes gives useless results
54 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
54 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
55 if not dumbip.startswith('127.') and ':' not in dumbip:
55 if not dumbip.startswith('127.') and ':' not in dumbip:
56 return dumbip
56 return dumbip
57
57
58 # works elsewhere, but actually sends a packet
58 # works elsewhere, but actually sends a packet
59 try:
59 try:
60 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
60 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
61 s.connect(('1.0.0.1', 1))
61 s.connect(('1.0.0.1', 1))
62 ip = s.getsockname()[0]
62 ip = s.getsockname()[0]
63 return ip
63 return ip
64 except:
64 except:
65 pass
65 pass
66
66
67 return dumbip
67 return dumbip
68
68
69 def publish(name, desc, path, port):
69 def publish(name, desc, path, port):
70 global server, localip
70 global server, localip
71 if not server:
71 if not server:
72 try:
72 try:
73 server = Zeroconf.Zeroconf()
73 server = Zeroconf.Zeroconf()
74 except socket.gaierror:
74 except socket.gaierror:
75 # if we have no internet connection, this can happen.
75 # if we have no internet connection, this can happen.
76 return
76 return
77 ip = getip()
77 ip = getip()
78 localip = socket.inet_aton(ip)
78 localip = socket.inet_aton(ip)
79
79
80 parts = socket.gethostname().split('.')
80 hostname = socket.gethostname().split('.')[0]
81 host = parts[0] + ".local"
81 host = hostname + ".local"
82 name = "%s-%s" % (hostname, name)
82
83
83 # advertise to browsers
84 # advertise to browsers
84 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
85 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
85 name + '._http._tcp.local.',
86 name + '._http._tcp.local.',
86 server = host,
87 server = host,
87 port = port,
88 port = port,
88 properties = {'description': desc,
89 properties = {'description': desc,
89 'path': "/" + path},
90 'path': "/" + path},
90 address = localip, weight = 0, priority = 0)
91 address = localip, weight = 0, priority = 0)
91 server.registerService(svc)
92 server.registerService(svc)
92
93
93 # advertise to Mercurial clients
94 # advertise to Mercurial clients
94 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
95 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
95 name + '._hg._tcp.local.',
96 name + '._hg._tcp.local.',
96 server = host,
97 server = host,
97 port = port,
98 port = port,
98 properties = {'description': desc,
99 properties = {'description': desc,
99 'path': "/" + path},
100 'path': "/" + path},
100 address = localip, weight = 0, priority = 0)
101 address = localip, weight = 0, priority = 0)
101 server.registerService(svc)
102 server.registerService(svc)
102
103
103 class hgwebzc(hgweb_mod.hgweb):
104 class hgwebzc(hgweb_mod.hgweb):
104 def __init__(self, repo, name=None):
105 def __init__(self, repo, name=None):
105 super(hgwebzc, self).__init__(repo, name)
106 super(hgwebzc, self).__init__(repo, name)
106 name = self.reponame or os.path.basename(repo.root)
107 name = self.reponame or os.path.basename(repo.root)
107 desc = self.repo.ui.config("web", "description", name)
108 desc = self.repo.ui.config("web", "description", name)
108 publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
109 publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
109
110
110 class hgwebdirzc(hgwebdir_mod.hgwebdir):
111 class hgwebdirzc(hgwebdir_mod.hgwebdir):
111 def run(self):
112 def run(self):
112 for r, p in self.repos:
113 for r, p in self.repos:
113 u = ui.ui(parentui=self.parentui)
114 u = ui.ui(parentui=self.parentui)
114 u.readconfig(os.path.join(p, '.hg', 'hgrc'))
115 u.readconfig(os.path.join(p, '.hg', 'hgrc'))
115 n = os.path.basename(r)
116 n = os.path.basename(r)
116 publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
117 publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
117 return super(hgwebdirzc, self).run()
118 return super(hgwebdirzc, self).run()
118
119
119 # listen
120 # listen
120
121
121 class listener(object):
122 class listener(object):
122 def __init__(self):
123 def __init__(self):
123 self.found = {}
124 self.found = {}
124 def removeService(self, server, type, name):
125 def removeService(self, server, type, name):
125 if repr(name) in self.found:
126 if repr(name) in self.found:
126 del self.found[repr(name)]
127 del self.found[repr(name)]
127 def addService(self, server, type, name):
128 def addService(self, server, type, name):
128 self.found[repr(name)] = server.getServiceInfo(type, name)
129 self.found[repr(name)] = server.getServiceInfo(type, name)
129
130
130 def getzcpaths():
131 def getzcpaths():
131 server = Zeroconf.Zeroconf()
132 server = Zeroconf.Zeroconf()
132 l = listener()
133 l = listener()
133 browser = Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
134 browser = Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
134 time.sleep(1)
135 time.sleep(1)
135 server.close()
136 server.close()
136 for v in l.found.values():
137 for v in l.found.values():
137 n = v.name[:v.name.index('.')]
138 n = v.name[:v.name.index('.')]
138 n.replace(" ", "-")
139 n.replace(" ", "-")
139 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port,
140 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port,
140 v.properties.get("path", "/"))
141 v.properties.get("path", "/"))
141 yield "zc-" + n, u
142 yield "zc-" + n, u
142
143
143 def config(orig, self, section, key, default=None, untrusted=False):
144 def config(orig, self, section, key, default=None, untrusted=False):
144 if section == "paths" and key.startswith("zc-"):
145 if section == "paths" and key.startswith("zc-"):
145 for n, p in getzcpaths():
146 for n, p in getzcpaths():
146 if n == key:
147 if n == key:
147 return p
148 return p
148 return orig(self, section, key, default, untrusted)
149 return orig(self, section, key, default, untrusted)
149
150
150 def configitems(orig, self, section, untrusted=False):
151 def configitems(orig, self, section, untrusted=False):
151 r = orig(self, section, untrusted)
152 r = orig(self, section, untrusted)
152 if section == "paths":
153 if section == "paths":
153 r += getzcpaths()
154 r += getzcpaths()
154 return r
155 return r
155
156
156 extensions.wrapfunction(ui.ui, 'config', config)
157 extensions.wrapfunction(ui.ui, 'config', config)
157 extensions.wrapfunction(ui.ui, 'configitems', configitems)
158 extensions.wrapfunction(ui.ui, 'configitems', configitems)
158 hgweb_mod.hgweb = hgwebzc
159 hgweb_mod.hgweb = hgwebzc
159 hgwebdir_mod.hgwebdir = hgwebdirzc
160 hgwebdir_mod.hgwebdir = hgwebdirzc
General Comments 0
You need to be logged in to leave comments. Login now