shill: Implement Modem1::GetLinkName()

Iterate over the links in /sys/class/net and find the one that
corresponds to the sysfs device path in our provided modem properties.

BUG=chromium-os:28498
TEST=Use a modem with an interface name other than usb0 (E362, or use
nameif to mangle usb0 into something else.

Change-Id: I16424c1d52f7fbac81c427750596e1f72be2a823
Reviewed-on: https://gerrit.chromium.org/gerrit/20102
Commit-Ready: Nathan J. Williams <njw@chromium.org>
Reviewed-by: Nathan J. Williams <njw@chromium.org>
Tested-by: Nathan J. Williams <njw@chromium.org>
Reviewed-by: Jason Glasgow <jglasgow@chromium.org>
diff --git a/modem_1.cc b/modem_1.cc
index 2d8ccd4..e8fc65f 100644
--- a/modem_1.cc
+++ b/modem_1.cc
@@ -4,6 +4,7 @@
 
 #include "shill/modem.h"
 
+#include <base/file_util.h>
 #include <mm/ModemManager-enums.h>
 #include <mm/ModemManager-names.h>
 
@@ -47,12 +48,43 @@
   }
 }
 
-bool Modem1::GetLinkName(const DBusPropertiesMap & /* modem_props */,
+bool Modem1::GetLinkName(const DBusPropertiesMap &modem_props,
                          string *name) const {
-  // TODO(rochberg): use the device path to find the link name in
-  // sysfs.  crosbug.com/28498
-  *name = "usb0";
-  return true;
+  string device_prop;
+  if (!DBusProperties::GetString(modem_props,
+                                 Modem::kPropertyLinkName,
+                                 &device_prop)) {
+    return false;
+  }
+
+  // |device_prop| will be a sysfs path such as:
+  //  /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-2
+  FilePath device_path(device_prop);
+
+  // Each entry in /sys/class/net has the name of a network interface
+  // and is a symlink into the actual device structure:
+  //  eth0 -> ../../devices/pci0000:00/0000:00:1c.5/0000:01:00.0/net/eth0
+  // Iterate over all of these and see if any of them point into
+  // subdirectories of the sysfs path from the Device property.
+  FilePath netfiles_path("/sys/class/net");
+
+  // FileEnumerator warns that it is a blocking interface; that
+  // shouldn't be a problem here.
+  file_util::FileEnumerator netfiles(netfiles_path,
+                                     false, // don't recurse
+                                     file_util::FileEnumerator::DIRECTORIES);
+  for (FilePath link = netfiles.Next(); !link.empty(); link = netfiles.Next()) {
+    FilePath target;
+    if (!file_util::ReadSymbolicLink(link, &target))
+      continue;
+    if (!target.IsAbsolute())
+      target = netfiles_path.Append(target);
+    if (file_util::ContainsPath(device_path, target)) {
+      *name = link.BaseName().value();
+      return true;
+    }
+  }
+  return false;
 }
 
 void Modem1::CreateDeviceMM1(const DBusInterfaceToProperties &i_to_p) {