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