]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/util/iputil.py
Make platform-detection code tolerate linux-3.0, patch by zooko.
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / util / iputil.py
index 203f172d10d012e2087ff8f36752231b67542aff..260bfcdbcf710c9780cc947ec15764743282fd84 100644 (file)
@@ -1,22 +1,12 @@
-# portions extracted from ipaddresslib by Autonomous Zone Industries, LGPL (author: Greg Smith)
-# portions adapted from nattraverso.ipdiscover
-# portions authored by Brian Warner, working for Allmydata
-# most recent version authored by Zooko O'Whielacronx, working for Allmydata
-
 # from the Python Standard Library
-import os, re, socket, sys
+import os, re, socket, sys, subprocess
 
 # from Twisted
-from twisted.internet import defer
-from twisted.internet import reactor
+from twisted.internet import defer, threads, reactor
 from twisted.internet.protocol import DatagramProtocol
-from twisted.internet.utils import getProcessOutput
 from twisted.python.procutils import which
 from twisted.python import log
 
-# from allmydata.util
-import observer
-
 try:
     import resource
     def increase_rlimits():
@@ -78,7 +68,7 @@ except ImportError:
     increase_rlimits = _increase_rlimits
 
 
-def get_local_addresses_async(target='A.ROOT-SERVERS.NET'):
+def get_local_addresses_async(target="198.41.0.4"): # A.ROOT-SERVERS.NET
     """
     Return a Deferred that fires with a list of IPv4 addresses (as dotted-quad
     strings) that are currently configured on this host, sorted in descending
@@ -120,7 +110,18 @@ def get_local_ip_for(target):
     try:
         target_ipaddr = socket.gethostbyname(target)
     except socket.gaierror:
-        # DNS isn't running
+        # DNS isn't running, or somehow we encountered an error
+
+        # note: if an interface is configured and up, but nothing is
+        # connected to it, gethostbyname("A.ROOT-SERVERS.NET") will take 20
+        # seconds to raise socket.gaierror . This is synchronous and occurs
+        # for each node being started, so users of
+        # test.common.SystemTestMixin (like test_system) will see something
+        # like 120s of delay, which may be enough to hit the default trial
+        # timeouts. For that reason, get_local_addresses_async() was changed
+        # to default to the numerical ip address for A.ROOT-SERVERS.NET, to
+        # avoid this DNS lookup. This also makes node startup fractionally
+        # faster.
         return None
     udpprot = DatagramProtocol()
     port = reactor.listenUDP(0, udpprot)
@@ -138,15 +139,27 @@ _platform_map = {
     "linux-i386": "linux", # redhat
     "linux-ppc": "linux",  # redhat
     "linux2": "linux",     # debian
+    "linux3": "linux",     # debian
     "win32": "win32",
     "irix6-n32": "irix",
     "irix6-n64": "irix",
     "irix6": "irix",
     "openbsd2": "bsd",
+    "openbsd3": "bsd",
+    "openbsd4": "bsd",
     "darwin": "bsd",       # Mac OS X
     "freebsd4": "bsd",
     "freebsd5": "bsd",
+    "freebsd6": "bsd",
+    "freebsd7": "bsd",
+    "freebsd8": "bsd",
+    "freebsd9": "bsd",
     "netbsd1": "bsd",
+    "netbsd2": "bsd",
+    "netbsd3": "bsd",
+    "netbsd4": "bsd",
+    "netbsd5": "bsd",
+    "netbsd6": "bsd",
     "sunos5": "sunos",
     "cygwin": "cygwin",
     }
@@ -164,12 +177,12 @@ _win32_re = re.compile('^\s*\d+\.\d+\.\d+\.\d+\s.+\s(?P<address>\d+\.\d+\.\d+\.\
 
 # These work in Redhat 6.x and Debian 2.2 potato
 _linux_path = '/sbin/ifconfig'
-_linux_re = re.compile('^\s*inet addr:(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
+_linux_re = re.compile('^\s*inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
 
 # NetBSD 1.4 (submitted by Rhialto), Darwin, Mac OS X
 _netbsd_path = '/sbin/ifconfig'
 _netbsd_args = ('-a',)
-_netbsd_re = re.compile('^\s+inet (?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
+_netbsd_re = re.compile('^\s+inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
 
 # Irix 6.5
 _irix_path = '/usr/etc/ifconfig'
@@ -177,39 +190,6 @@ _irix_path = '/usr/etc/ifconfig'
 # Solaris 2.x
 _sunos_path = '/usr/sbin/ifconfig'
 
-class SequentialTrier(object):
-    """ I hold a list of executables to try and try each one in turn
-    until one gives me a list of IP addresses."""
-
-    def __init__(self, exebasename, args, regex):
-        assert not os.path.isabs(exebasename)
-        self.exes_left_to_try = which(exebasename)
-        self.exes_left_to_try.reverse()
-        self.args = args
-        self.regex = regex
-        self.o = observer.OneShotObserverList()
-        self._try_next()
-
-    def _try_next(self):
-        if not self.exes_left_to_try:
-            self.o.fire(None)
-        else:
-            exe = self.exes_left_to_try.pop()
-            d2 = _query(exe, self.args, self.regex)
-
-            def cb(res):
-                if res:
-                    self.o.fire(res)
-                else:
-                    self._try_next()
-
-            def eb(why):
-                self._try_next()
-
-            d2.addCallbacks(cb, eb)
-
-    def when_tried(self):
-        return self.o.when_fired()
 
 # k: platform string as provided in the value of _platform_map
 # v: tuple of (path_to_tool, args, regex,)
@@ -221,7 +201,11 @@ _tool_map = {
     "irix": (_irix_path, _netbsd_args, _netbsd_re,),
     "sunos": (_sunos_path, _netbsd_args, _netbsd_re,),
     }
+
 def _find_addresses_via_config():
+    return threads.deferToThread(_synchronously_find_addresses_via_config)
+
+def _synchronously_find_addresses_via_config():
     # originally by Greg Smith, hacked by Zooko to conform to Brian's API
 
     platform = _platform_map.get(sys.platform)
@@ -238,23 +222,31 @@ def _find_addresses_via_config():
     if os.path.isabs(pathtotool):
         return _query(pathtotool, args, regex)
     else:
-        return SequentialTrier(pathtotool, args, regex).when_tried()
+        exes_to_try = which(pathtotool)
+        for exe in exes_to_try:
+            try:
+                addresses = _query(exe, args, regex)
+            except Exception:
+                addresses = []
+            if addresses:
+                return addresses
+        return []
 
 def _query(path, args, regex):
-    d = getProcessOutput(path, args)
-    def _parse(output):
-        addresses = []
-        outputsplit = output.split('\n')
-        for outline in outputsplit:
-            m = regex.match(outline)
-            if m:
-                addr = m.groupdict()['address']
-                if addr not in addresses:
-                    addresses.append(addr)
+    env = {'LANG': 'en_US.UTF-8'}
+    p = subprocess.Popen([path] + list(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
+    (output, err) = p.communicate()
 
-        return addresses
-    d.addCallback(_parse)
-    return d
+    addresses = []
+    outputsplit = output.split('\n')
+    for outline in outputsplit:
+        m = regex.match(outline)
+        if m:
+            addr = m.groupdict()['address']
+            if addr not in addresses:
+                addresses.append(addr)
+
+    return addresses
 
 def _cygwin_hack_find_addresses(target):
     addresses = []