Home | History | Annotate | Download | only in music_manager_private
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/extensions/api/music_manager_private/device_id.h"
      6 
      7 #include <sys/socket.h>  // Must be included before ifaddrs.h.
      8 #include <ifaddrs.h>
      9 #include <net/if.h>
     10 #include <sys/ioctl.h>
     11 
     12 #include <map>
     13 
     14 #include "base/bind.h"
     15 #include "base/files/file_enumerator.h"
     16 #include "base/files/file_path.h"
     17 #include "base/files/file_util.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/strings/string_util.h"
     20 #include "base/threading/thread_restrictions.h"
     21 #include "content/public/browser/browser_thread.h"
     22 
     23 namespace {
     24 
     25 using extensions::api::DeviceId;
     26 
     27 typedef base::Callback<bool(const void* bytes, size_t size)>
     28     IsValidMacAddressCallback;
     29 
     30 const char kDiskByUuidDirectoryName[] = "/dev/disk/by-uuid";
     31 const char* kDeviceNames[] = {
     32   "sda1", "hda1", "dm-0", "xvda1", "sda2", "hda2", "dm-1", "xvda2",
     33 };
     34 // Fedora 15 uses biosdevname feature where Embedded ethernet uses the
     35 // "em" prefix and PCI cards use the p[0-9]c[0-9] format based on PCI
     36 // slot and card information.
     37 const char* kNetDeviceNamePrefixes[] = {
     38    "eth", "em", "en", "wl", "ww", "p0", "p1", "p2", "p3", "p4", "p5", "p6",
     39    "p7", "p8", "p9", "wlan"
     40 };
     41 
     42 // Map from device name to disk uuid
     43 typedef std::map<base::FilePath, base::FilePath> DiskEntries;
     44 
     45 std::string GetDiskUuid() {
     46   base::ThreadRestrictions::AssertIOAllowed();
     47 
     48   DiskEntries disk_uuids;
     49   base::FileEnumerator files(base::FilePath(kDiskByUuidDirectoryName),
     50                              false,  // Recursive.
     51                              base::FileEnumerator::FILES);
     52   do {
     53     base::FilePath file_path = files.Next();
     54     if (file_path.empty())
     55       break;
     56 
     57     base::FilePath target_path;
     58     if (!base::ReadSymbolicLink(file_path, &target_path))
     59       continue;
     60 
     61     base::FilePath device_name = target_path.BaseName();
     62     base::FilePath disk_uuid = file_path.BaseName();
     63     disk_uuids[device_name] = disk_uuid;
     64   } while (true);
     65 
     66   // Look for first device name matching an entry of |kDeviceNames|.
     67   std::string result;
     68   for (size_t i = 0; i < arraysize(kDeviceNames); i++) {
     69     DiskEntries::iterator it =
     70         disk_uuids.find(base::FilePath(kDeviceNames[i]));
     71     if (it != disk_uuids.end()) {
     72       DVLOG(1) << "Returning uuid: \"" << it->second.value()
     73                << "\" for device \"" << it->first.value() << "\"";
     74       result = it->second.value();
     75       break;
     76     }
     77   }
     78 
     79   // Log failure (at most once) for diagnostic purposes.
     80   static bool error_logged = false;
     81   if (result.empty() && !error_logged) {
     82     error_logged = true;
     83     LOG(ERROR) << "Could not find appropriate disk uuid.";
     84     for (DiskEntries::iterator it = disk_uuids.begin();
     85         it != disk_uuids.end(); ++it) {
     86       LOG(ERROR) << "  DeviceID=" << it->first.value() << ", uuid="
     87                  << it->second.value();
     88     }
     89   }
     90 
     91   return result;
     92 }
     93 
     94 class MacAddressProcessor {
     95  public:
     96   explicit MacAddressProcessor(
     97       const IsValidMacAddressCallback& is_valid_mac_address)
     98       : is_valid_mac_address_(is_valid_mac_address) {
     99   }
    100 
    101   bool ProcessInterface(struct ifaddrs *ifaddr,
    102                         const char* prefixes[],
    103                         size_t prefixes_count) {
    104     const int MAC_LENGTH = 6;
    105     struct ifreq ifinfo;
    106 
    107     memset(&ifinfo, 0, sizeof(ifinfo));
    108     strncpy(ifinfo.ifr_name, ifaddr->ifa_name, sizeof(ifinfo.ifr_name) - 1);
    109 
    110     int sd = socket(AF_INET, SOCK_DGRAM, 0);
    111     int result = ioctl(sd, SIOCGIFHWADDR, &ifinfo);
    112     close(sd);
    113 
    114     if (result != 0)
    115       return true;
    116 
    117     const char* mac_address =
    118         static_cast<const char*>(ifinfo.ifr_hwaddr.sa_data);
    119     if (!is_valid_mac_address_.Run(mac_address, MAC_LENGTH))
    120       return true;
    121 
    122     if (!IsValidPrefix(ifinfo.ifr_name, prefixes, prefixes_count))
    123       return true;
    124 
    125     // Got one!
    126     found_mac_address_ =
    127         base::StringToLowerASCII(base::HexEncode(mac_address, MAC_LENGTH));
    128     return false;
    129   }
    130 
    131   std::string mac_address() const { return found_mac_address_; }
    132 
    133  private:
    134   bool IsValidPrefix(const char* name,
    135                      const char* prefixes[],
    136                      size_t prefixes_count) {
    137     for (size_t i = 0; i < prefixes_count; i++) {
    138       if (strncmp(prefixes[i], name, strlen(prefixes[i])) == 0)
    139         return true;
    140     }
    141     return false;
    142   }
    143 
    144   const IsValidMacAddressCallback& is_valid_mac_address_;
    145   std::string found_mac_address_;
    146 };
    147 
    148 std::string GetMacAddress(
    149     const IsValidMacAddressCallback& is_valid_mac_address) {
    150   base::ThreadRestrictions::AssertIOAllowed();
    151 
    152   struct ifaddrs* ifaddrs;
    153   int rv = getifaddrs(&ifaddrs);
    154   if (rv < 0) {
    155     PLOG(ERROR) << "getifaddrs failed " << rv;
    156     return "";
    157   }
    158 
    159   MacAddressProcessor processor(is_valid_mac_address);
    160   for (struct ifaddrs* ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
    161     bool keep_going = processor.ProcessInterface(
    162         ifa, kNetDeviceNamePrefixes, arraysize(kNetDeviceNamePrefixes));
    163     if (!keep_going)
    164       break;
    165   }
    166   freeifaddrs(ifaddrs);
    167   return processor.mac_address();
    168 }
    169 
    170 void GetRawDeviceIdImpl(const IsValidMacAddressCallback& is_valid_mac_address,
    171                         const DeviceId::IdCallback& callback) {
    172   base::ThreadRestrictions::AssertIOAllowed();
    173 
    174   std::string disk_id = GetDiskUuid();
    175   std::string mac_address = GetMacAddress(is_valid_mac_address);
    176 
    177   std::string raw_device_id;
    178   if (!mac_address.empty() && !disk_id.empty()) {
    179     raw_device_id = mac_address + disk_id;
    180   }
    181 
    182   content::BrowserThread::PostTask(
    183       content::BrowserThread::UI,
    184       FROM_HERE,
    185       base::Bind(callback, raw_device_id));
    186 }
    187 
    188 }  // namespace
    189 
    190 namespace extensions {
    191 namespace api {
    192 
    193 // static
    194 void DeviceId::GetRawDeviceId(const IdCallback& callback) {
    195   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    196 
    197   content::BrowserThread::PostTask(
    198       content::BrowserThread::FILE,
    199       FROM_HERE,
    200       base::Bind(GetRawDeviceIdImpl,
    201           base::Bind(DeviceId::IsValidMacAddress),
    202           callback));
    203 }
    204 
    205 }  // namespace api
    206 }  // namespace extensions
    207