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.
5 #include "chrome/browser/extensions/api/music_manager_private/device_id.h"
7 #include <sys/socket.h> // Must be included before ifaddrs.h.
10 #include <sys/ioctl.h>
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"
25 using extensions::api::DeviceId
;
27 typedef base::Callback
<bool(const void* bytes
, size_t size
)>
28 IsValidMacAddressCallback
;
30 const char kDiskByUuidDirectoryName
[] = "/dev/disk/by-uuid";
31 const char* const kDeviceNames
[] = {
32 "sda1", "hda1", "dm-0", "xvda1", "sda2", "hda2", "dm-1", "xvda2",
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* const kNetDeviceNamePrefixes
[] = {
38 "eth", "em", "en", "wl", "ww", "p0", "p1", "p2", "p3", "p4", "p5", "p6",
39 "p7", "p8", "p9", "wlan"
42 // Map from device name to disk uuid
43 typedef std::map
<base::FilePath
, base::FilePath
> DiskEntries
;
45 std::string
GetDiskUuid() {
46 base::ThreadRestrictions::AssertIOAllowed();
48 DiskEntries disk_uuids
;
49 base::FileEnumerator
files(base::FilePath(kDiskByUuidDirectoryName
),
51 base::FileEnumerator::FILES
);
53 base::FilePath file_path
= files
.Next();
54 if (file_path
.empty())
57 base::FilePath target_path
;
58 if (!base::ReadSymbolicLink(file_path
, &target_path
))
61 base::FilePath device_name
= target_path
.BaseName();
62 base::FilePath disk_uuid
= file_path
.BaseName();
63 disk_uuids
[device_name
] = disk_uuid
;
66 // Look for first device name matching an entry of |kDeviceNames|.
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();
79 // Log failure (at most once) for diagnostic purposes.
80 static bool error_logged
= false;
81 if (result
.empty() && !error_logged
) {
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();
94 class MacAddressProcessor
{
96 explicit MacAddressProcessor(
97 const IsValidMacAddressCallback
& is_valid_mac_address
)
98 : is_valid_mac_address_(is_valid_mac_address
) {
101 bool ProcessInterface(struct ifaddrs
*ifaddr
,
102 const char* const prefixes
[],
103 size_t prefixes_count
) {
104 const int MAC_LENGTH
= 6;
107 memset(&ifinfo
, 0, sizeof(ifinfo
));
108 strncpy(ifinfo
.ifr_name
, ifaddr
->ifa_name
, sizeof(ifinfo
.ifr_name
) - 1);
110 int sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
111 int result
= ioctl(sd
, SIOCGIFHWADDR
, &ifinfo
);
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
))
122 if (!IsValidPrefix(ifinfo
.ifr_name
, prefixes
, prefixes_count
))
127 base::ToLowerASCII(base::HexEncode(mac_address
, MAC_LENGTH
));
131 std::string
mac_address() const { return found_mac_address_
; }
134 bool IsValidPrefix(const char* name
,
135 const char* const 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)
144 const IsValidMacAddressCallback
& is_valid_mac_address_
;
145 std::string found_mac_address_
;
148 std::string
GetMacAddress(
149 const IsValidMacAddressCallback
& is_valid_mac_address
) {
150 base::ThreadRestrictions::AssertIOAllowed();
152 struct ifaddrs
* ifaddrs
;
153 int rv
= getifaddrs(&ifaddrs
);
155 PLOG(ERROR
) << "getifaddrs failed " << rv
;
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
));
166 freeifaddrs(ifaddrs
);
167 return processor
.mac_address();
170 void GetRawDeviceIdImpl(const IsValidMacAddressCallback
& is_valid_mac_address
,
171 const DeviceId::IdCallback
& callback
) {
172 base::ThreadRestrictions::AssertIOAllowed();
174 std::string disk_id
= GetDiskUuid();
175 std::string mac_address
= GetMacAddress(is_valid_mac_address
);
177 std::string raw_device_id
;
178 if (!mac_address
.empty() && !disk_id
.empty()) {
179 raw_device_id
= mac_address
+ disk_id
;
182 content::BrowserThread::PostTask(
183 content::BrowserThread::UI
,
185 base::Bind(callback
, raw_device_id
));
190 namespace extensions
{
194 void DeviceId::GetRawDeviceId(const IdCallback
& callback
) {
195 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
197 content::BrowserThread::PostTask(
198 content::BrowserThread::FILE,
200 base::Bind(GetRawDeviceIdImpl
,
201 base::Bind(DeviceId::IsValidMacAddress
),
206 } // namespace extensions