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