##// END OF EJS Templates
zeroconf: Don't break serve if no internet connection is present.
Augie Fackler -
r7295:66d0fc10 default
parent child Browse files
Show More
@@ -1,132 +1,136
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 import Zeroconf, socket, time, os
9 import Zeroconf, socket, time, os
10 from mercurial import ui
10 from mercurial import ui
11 from mercurial import extensions
11 from mercurial import extensions
12 from mercurial.hgweb import hgweb_mod
12 from mercurial.hgweb import hgweb_mod
13 from mercurial.hgweb import hgwebdir_mod
13 from mercurial.hgweb import hgwebdir_mod
14
14
15 # publish
15 # publish
16
16
17 server = None
17 server = None
18 localip = None
18 localip = None
19
19
20 def getip():
20 def getip():
21 # finds external-facing interface without sending any packets (Linux)
21 # finds external-facing interface without sending any packets (Linux)
22 try:
22 try:
23 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
23 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
24 s.connect(('1.0.0.1', 0))
24 s.connect(('1.0.0.1', 0))
25 ip = s.getsockname()[0]
25 ip = s.getsockname()[0]
26 return ip
26 return ip
27 except:
27 except:
28 pass
28 pass
29
29
30 # Generic method, sometimes gives useless results
30 # Generic method, sometimes gives useless results
31 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
31 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
32 if not dumbip.startswith('127.'):
32 if not dumbip.startswith('127.'):
33 return dumbip
33 return dumbip
34
34
35 # works elsewhere, but actually sends a packet
35 # works elsewhere, but actually sends a packet
36 try:
36 try:
37 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
37 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
38 s.connect(('1.0.0.1', 1))
38 s.connect(('1.0.0.1', 1))
39 ip = s.getsockname()[0]
39 ip = s.getsockname()[0]
40 return ip
40 return ip
41 except:
41 except:
42 pass
42 pass
43
43
44 return dumbip
44 return dumbip
45
45
46 def publish(name, desc, path, port):
46 def publish(name, desc, path, port):
47 global server, localip
47 global server, localip
48 if not server:
48 if not server:
49 try:
49 server = Zeroconf.Zeroconf()
50 server = Zeroconf.Zeroconf()
51 except socket.gaierror:
52 # if we have no internet connection, this can happen.
53 return
50 ip = getip()
54 ip = getip()
51 localip = socket.inet_aton(ip)
55 localip = socket.inet_aton(ip)
52
56
53 parts = socket.gethostname().split('.')
57 parts = socket.gethostname().split('.')
54 host = parts[0] + ".local"
58 host = parts[0] + ".local"
55
59
56 # advertise to browsers
60 # advertise to browsers
57 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
61 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
58 name + '._http._tcp.local.',
62 name + '._http._tcp.local.',
59 server = host,
63 server = host,
60 port = port,
64 port = port,
61 properties = {'description': desc,
65 properties = {'description': desc,
62 'path': "/" + path},
66 'path': "/" + path},
63 address = localip, weight = 0, priority = 0)
67 address = localip, weight = 0, priority = 0)
64 server.registerService(svc)
68 server.registerService(svc)
65
69
66 # advertise to Mercurial clients
70 # advertise to Mercurial clients
67 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
71 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
68 name + '._hg._tcp.local.',
72 name + '._hg._tcp.local.',
69 server = host,
73 server = host,
70 port = port,
74 port = port,
71 properties = {'description': desc,
75 properties = {'description': desc,
72 'path': "/" + path},
76 'path': "/" + path},
73 address = localip, weight = 0, priority = 0)
77 address = localip, weight = 0, priority = 0)
74 server.registerService(svc)
78 server.registerService(svc)
75
79
76 class hgwebzc(hgweb_mod.hgweb):
80 class hgwebzc(hgweb_mod.hgweb):
77 def __init__(self, repo, name=None):
81 def __init__(self, repo, name=None):
78 super(hgwebzc, self).__init__(repo, name)
82 super(hgwebzc, self).__init__(repo, name)
79 name = self.reponame or os.path.basename(repo.root)
83 name = self.reponame or os.path.basename(repo.root)
80 desc = self.repo.ui.config("web", "description", name)
84 desc = self.repo.ui.config("web", "description", name)
81 publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
85 publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
82
86
83 class hgwebdirzc(hgwebdir_mod.hgwebdir):
87 class hgwebdirzc(hgwebdir_mod.hgwebdir):
84 def run(self):
88 def run(self):
85 for r, p in self.repos:
89 for r, p in self.repos:
86 u = ui.ui(parentui=self.parentui)
90 u = ui.ui(parentui=self.parentui)
87 u.readconfig(os.path.join(p, '.hg', 'hgrc'))
91 u.readconfig(os.path.join(p, '.hg', 'hgrc'))
88 n = os.path.basename(r)
92 n = os.path.basename(r)
89 publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
93 publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
90 return super(hgwebdirzc, self).run()
94 return super(hgwebdirzc, self).run()
91
95
92 # listen
96 # listen
93
97
94 class listener(object):
98 class listener(object):
95 def __init__(self):
99 def __init__(self):
96 self.found = {}
100 self.found = {}
97 def removeService(self, server, type, name):
101 def removeService(self, server, type, name):
98 if repr(name) in self.found:
102 if repr(name) in self.found:
99 del self.found[repr(name)]
103 del self.found[repr(name)]
100 def addService(self, server, type, name):
104 def addService(self, server, type, name):
101 self.found[repr(name)] = server.getServiceInfo(type, name)
105 self.found[repr(name)] = server.getServiceInfo(type, name)
102
106
103 def getzcpaths():
107 def getzcpaths():
104 server = Zeroconf.Zeroconf()
108 server = Zeroconf.Zeroconf()
105 l = listener()
109 l = listener()
106 browser = Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
110 browser = Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
107 time.sleep(1)
111 time.sleep(1)
108 server.close()
112 server.close()
109 for v in l.found.values():
113 for v in l.found.values():
110 n = v.name[:v.name.index('.')]
114 n = v.name[:v.name.index('.')]
111 n.replace(" ", "-")
115 n.replace(" ", "-")
112 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port,
116 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port,
113 v.properties.get("path", "/"))
117 v.properties.get("path", "/"))
114 yield "zc-" + n, u
118 yield "zc-" + n, u
115
119
116 def config(orig, self, section, key, default=None, untrusted=False):
120 def config(orig, self, section, key, default=None, untrusted=False):
117 if section == "paths" and key.startswith("zc-"):
121 if section == "paths" and key.startswith("zc-"):
118 for n, p in getzcpaths():
122 for n, p in getzcpaths():
119 if n == key:
123 if n == key:
120 return p
124 return p
121 return orig(self, section, key, default, untrusted)
125 return orig(self, section, key, default, untrusted)
122
126
123 def configitems(orig, self, section, untrusted=False):
127 def configitems(orig, self, section, untrusted=False):
124 r = orig(self, section, untrusted)
128 r = orig(self, section, untrusted)
125 if section == "paths":
129 if section == "paths":
126 r += getzcpaths()
130 r += getzcpaths()
127 return r
131 return r
128
132
129 extensions.wrapfunction(ui.ui, 'config', config)
133 extensions.wrapfunction(ui.ui, 'config', config)
130 extensions.wrapfunction(ui.ui, 'configitems', configitems)
134 extensions.wrapfunction(ui.ui, 'configitems', configitems)
131 hgweb_mod.hgweb = hgwebzc
135 hgweb_mod.hgweb = hgwebzc
132 hgwebdir_mod.hgwebdir = hgwebdirzc
136 hgwebdir_mod.hgwebdir = hgwebdirzc
General Comments 0
You need to be logged in to leave comments. Login now