Merge "/bin/conman:  Run ifplugd script for eth0."
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 9bbfb78..c484d4a 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -214,11 +214,11 @@
     # explicitly.
     if os.path.exists(os.path.join(self._interface_status_dir,
                                    self.ETHERNET_STATUS_FILE)):
-      self._ethernet_file_exists = True
       self._process_file(self._interface_status_dir, self.ETHERNET_STATUS_FILE)
     else:
-      self._ethernet_file_exists = False
-      self.bridge.ethernet = self.is_ethernet_up()
+      ethernet_up = self.is_ethernet_up()
+      self.bridge.ethernet = ethernet_up
+      self.ifplugd_action('eth0', ethernet_up)
 
     for path, prefix in ((self._moca_status_dir, self.MOCA_NODE_FILE_PREFIX),
                          (self._status_dir, self.COMMAND_FILE_PREFIX),
@@ -402,11 +402,6 @@
     return self.bridge.internet() or any(wifi.internet() for wifi in self.wifi)
 
   def _update_interfaces_and_routes(self):
-    # If ifplugd has never written an ethernet status file, poll manually before
-    # updating routes.
-    if not self._ethernet_file_exists:
-      self.bridge.ethernet = self.is_ethernet_up()
-
     self.bridge.update_routes()
     for wifi in self.wifi:
       wifi.update_routes()
@@ -457,7 +452,6 @@
       if filename == self.ETHERNET_STATUS_FILE:
         try:
           self.bridge.ethernet = bool(int(contents))
-          self._ethernet_file_exists = True
         except ValueError:
           logging.error('Status file contents should be 0 or 1, not %s',
                         contents)
@@ -502,15 +496,16 @@
           self.bridge.add_moca_station(node)
         has_moca = self.bridge.moca
         if had_moca != has_moca:
-          self.tell_ifplugd_about_moca(has_moca)
+          self.ifplugd_action('moca0', has_moca)
 
   def interface_by_name(self, interface_name):
     for ifc in [self.bridge] + self.wifi:
       if ifc.name == interface_name:
         return ifc
 
-  def tell_ifplugd_about_moca(self, up):
-    subprocess.call(self.IFPLUGD_ACTION + ['moca0', 'up' if up else 'down'])
+  def ifplugd_action(self, interface_name, up):
+    subprocess.call(self.IFPLUGD_ACTION + [interface_name,
+                                           'up' if up else 'down'])
 
   def _wifi_scan(self, wifi):
     """Perform a wifi scan and update wifi.cycler."""
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index 5388b22..afc5c9a 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -178,6 +178,8 @@
   WIFI_SETCLIENT = ['echo', 'setclient']
   IFUP = ['echo', 'ifup']
   IFPLUGD_ACTION = ['echo', 'ifplugd.action']
+  # This simulates the output of 'ip link' when eth0 is up.
+  IP_LINK = ['echo', 'eth0 LOWER_UP']
 
   def __init__(self, *args, **kwargs):
     super(ConnectionManager, self).__init__(*args, **kwargs)
@@ -216,8 +218,7 @@
       else:
         open(socket, 'w')
       wifi.set_connection_check_result('restricted')
-      self.write_interface_status_file(wifi.name, '1')
-      self.write_gateway_file(wifi.name)
+      self.ifplugd_action(wifi.name, True)
       return True
 
     return False
@@ -251,10 +252,16 @@
     super(ConnectionManager, self)._wifi_scan(wifi)
     wifi.wifi_scan_counter += 1
 
-  def tell_ifplugd_about_moca(self, up):
+  def ifplugd_action(self, interface_name, up):
     # Typically, when moca comes up, conman calls ifplugd.action, which writes
-    # this file.
-    self.write_interface_status_file('moca0', '1' if up else '0')
+    # this file.  Also, when conman starts, it calls ifplugd.action for eth0.
+    self.write_interface_status_file(interface_name, '1' if up else '0')
+
+    # ifplugd calls run-dhclient, which results in a gateway file if the link is
+    # up (and working).
+    if up:
+      self.write_gateway_file('br0' if interface_name in ('eth0', 'moca0')
+                              else interface_name)
 
   # Non-overrides
 
@@ -317,12 +324,7 @@
       f.write(value)
 
   def set_ethernet(self, up):
-    self.write_interface_status_file('eth0', '1' if up else '0')
-
-    if up:
-      # On the real system, ifplugd would run dhclient, which would write a
-      # gateway file.
-      self.write_gateway_file('br0')
+    self.ifplugd_action('eth0', up)
 
   def set_moca(self, up):
     moca_node1_file = os.path.join(self._moca_status_dir,
@@ -331,11 +333,6 @@
       f.write(FAKE_MOCA_NODE1_FILE if up else
               FAKE_MOCA_NODE1_FILE_DISCONNECTED)
 
-    if up:
-      # On the real system, ifplugd would run dhclient, which would write a
-      # gateway file.
-      self.write_gateway_file('br0')
-
   def run_until_interface_update(self):
     while self._interface_update_counter == 0:
       self.run_once()
@@ -407,15 +404,12 @@
     c:  A ConnectionManager set up by @connection_manager_test.
   """
 
-  # Initially, no access.
-  wvtest.WVFAIL(c.acs())
-  wvtest.WVFAIL(c.internet())
+  # Initially, there is ethernet access (via explicit check of ethernet status,
+  # rather than the interface status file).
+  wvtest.WVPASS(c.acs())
+  wvtest.WVPASS(c.internet())
 
   c.run_once()
-
-  # Bring up ethernet, access.
-  c.set_ethernet(True)
-  c.run_once()
   wvtest.WVPASS(c.acs())
   wvtest.WVPASS(c.internet())
   wvtest.WVPASS(c.bridge.current_route())
diff --git a/conman/interface.py b/conman/interface.py
index c2de318..d814950 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -48,10 +48,13 @@
       Whether the connection is working.
     """
     if not self.links:
+      logging.debug('Connection check for %s failed due to no links', self.name)
       return False
 
-    logging.debug('gateway ip for %s is %s', self.name, self._gateway_ip)
+    logging.debug('Gateway ip for %s is %s', self.name, self._gateway_ip)
     if self._gateway_ip is None:
+      logging.debug('Connection check for %s failed due to no gateway IP',
+                    self.name)
       return False
 
     # Temporarily add a route to make sure the connection check can be run.
@@ -65,6 +68,7 @@
                      'via', self._gateway_ip,
                      'dev', self.name,
                      'metric', str(METRIC_TEMPORARY_CONNECTION_CHECK))
+      added_temporary_route = True
 
     cmd = [self.CONNECTION_CHECK, '-I', self.name]
     if check_acs:
@@ -72,6 +76,10 @@
 
     with open(os.devnull, 'w') as devnull:
       result = subprocess.call(cmd, stdout=devnull, stderr=devnull) == 0
+      logging.debug('Connection check%s for %s %s',
+                    ' (ACS)' if check_acs else '',
+                    self.name,
+                    'passed' if result else 'failed')
 
     # Delete the temporary route.
     if added_temporary_route:
@@ -205,22 +213,27 @@
       deciding whether to add or remove routes.
     """
     logging.debug('Updating routes for %s', self.name)
-    # We care about the distinction between None (unknown) and False (known
-    # inaccessible) here.
-    # pylint: disable=g-explicit-bool-comparison
-    maybe_had_acs = self._has_acs != False
-    maybe_had_internet = self._has_internet != False
+    maybe_had_acs = self._has_acs
+    maybe_had_internet = self._has_internet
 
     if expire_cache:
       self.expire_connection_status_cache()
 
     has_acs = self.acs()
     has_internet = self.internet()
-    if ((not maybe_had_acs and has_acs) or
-        (not maybe_had_internet and has_internet)):
+
+    # This is a little confusing:  We want to try adding a route if we _may_
+    # have gone from no access to some access, and we want to try deleting the
+    # route if we _may_ have lost *all* access. So the first condition checks
+    # for truthiness but the elif checks for explicit Falsity (i.e. excluding
+    # the None/unknown case).
+    had_access = maybe_had_acs or maybe_had_internet
+    # pylint: disable=g-explicit-bool-comparison
+    maybe_had_access = maybe_had_acs != False or maybe_had_internet != False
+    has_access = has_acs or has_internet
+    if not had_access and has_access:
       self.add_route()
-    elif ((maybe_had_acs or maybe_had_internet) and not
-          (has_acs or has_internet)):
+    elif maybe_had_access and not has_access:
       self.delete_route()
 
 
diff --git a/conman/interface_test.py b/conman/interface_test.py
index edc3541..9905fea 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -54,6 +54,7 @@
           if k[0] == key[0]:
             logging.debug('Deleting route for %r (generalized from %s)', k, key)
             del self.routing_table[k]
+            break
 
 
 class Bridge(FakeInterfaceMixin, interface.Bridge):