Index: util/mac/mac_util.cc |
diff --git a/util/mac/mac_util.cc b/util/mac/mac_util.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..da6930c7ec210059e2f7184b76617ddb40f7b168 |
--- /dev/null |
+++ b/util/mac/mac_util.cc |
@@ -0,0 +1,282 @@ |
+// Copyright 2014 The Crashpad Authors. All rights reserved. |
+// |
+// Licensed under the Apache License, Version 2.0 (the "License"); |
+// you may not use this file except in compliance with the License. |
+// You may obtain a copy of the License at |
+// |
+// http://www.apache.org/licenses/LICENSE-2.0 |
+// |
+// Unless required by applicable law or agreed to in writing, software |
+// distributed under the License is distributed on an "AS IS" BASIS, |
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+// See the License for the specific language governing permissions and |
+// limitations under the License. |
+ |
+#include "util/mac/mac_util.h" |
+ |
+#include <CoreFoundation/CoreFoundation.h> |
+#include <IOKit/IOKitLib.h> |
+#include <string.h> |
+#include <sys/types.h> |
+#include <sys/utsname.h> |
+ |
+#include "base/logging.h" |
+#include "base/mac/foundation_util.h" |
+#include "base/mac/scoped_cftyperef.h" |
+#include "base/mac/scoped_ioobject.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_piece.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/strings/sys_string_conversions.h" |
+ |
+extern "C" { |
+// Private CoreFoundation internals. See 10.9.2 CF-855.14/CFPriv.h and |
+// CF-855.14/CFUtilities.c. These are marked for weak import because they’re |
+// private and subject to change. |
+ |
+#define WEAK_IMPORT __attribute__((weak_import)) |
+ |
+// Don’t call these functions directly, call them through the |
+// TryCFCopy*VersionDictionary() helpers to account for the possibility that |
+// they may not be present at runtime. |
+CFDictionaryRef _CFCopySystemVersionDictionary() WEAK_IMPORT; |
+CFDictionaryRef _CFCopyServerVersionDictionary() WEAK_IMPORT; |
+ |
+// Don’t use these constants with CFDictionaryGetValue() directly, use them with |
+// the TryCFDictionaryGetValue() wrapper to account for the possibility that |
+// they may not be present at runtime. |
+extern const CFStringRef _kCFSystemVersionProductNameKey WEAK_IMPORT; |
+extern const CFStringRef _kCFSystemVersionProductVersionKey WEAK_IMPORT; |
+extern const CFStringRef _kCFSystemVersionProductVersionExtraKey WEAK_IMPORT; |
+extern const CFStringRef _kCFSystemVersionBuildVersionKey WEAK_IMPORT; |
+ |
+#undef WEAK_IMPORT |
+ |
+} // extern "C" |
+ |
+namespace { |
+ |
+// Returns the running system’s Darwin major version. Don’t call this, it’s an |
+// implementation detail and its result is meant to be cached by |
+// MacOSXMinorVersion(). |
+// |
+// This is very similar to Chromium’s base/mac/mac_util.mm |
+// DarwinMajorVersionInternal(). |
+int DarwinMajorVersion() { |
+ // base::OperatingSystemVersionNumbers calls Gestalt(), which is a |
+ // higher-level function than is needed. It might perform unnecessary |
+ // operations. On 10.6, it was observed to be able to spawn threads (see |
+ // http://crbug.com/53200). It might also read files or perform other blocking |
+ // operations. Actually, nobody really knows for sure just what Gestalt() |
+ // might do, or what it might be taught to do in the future. |
+ // |
+ // uname(), on the other hand, is implemented as a simple series of sysctl() |
+ // system calls to obtain the relevant data from the kernel. The data is |
+ // compiled right into the kernel, so no threads or blocking or other funny |
+ // business is necessary. |
+ |
+ utsname uname_info; |
+ int rv = uname(&uname_info); |
+ PCHECK(rv == 0) << "uname"; |
+ |
+ DCHECK_EQ(strcmp(uname_info.sysname, "Darwin"), 0) << "unexpected sysname " |
+ << uname_info.sysname; |
+ |
+ char* dot = strchr(uname_info.release, '.'); |
+ CHECK(dot); |
+ |
+ int darwin_major_version = 0; |
+ CHECK(base::StringToInt( |
+ base::StringPiece(uname_info.release, dot - uname_info.release), |
+ &darwin_major_version)); |
+ |
+ return darwin_major_version; |
+} |
+ |
+// Helpers for the weak-imported private CoreFoundation internals. |
+ |
+CFDictionaryRef TryCFCopySystemVersionDictionary() { |
+ if (_CFCopySystemVersionDictionary) { |
+ return _CFCopySystemVersionDictionary(); |
+ } |
+ return NULL; |
+} |
+ |
+CFDictionaryRef TryCFCopyServerVersionDictionary() { |
+ if (_CFCopyServerVersionDictionary) { |
+ return _CFCopyServerVersionDictionary(); |
+ } |
+ return NULL; |
+} |
+ |
+const void* TryCFDictionaryGetValue(CFDictionaryRef dictionary, |
+ const void* value) { |
+ if (value) { |
+ return CFDictionaryGetValue(dictionary, value); |
+ } |
+ return NULL; |
+} |
+ |
+// Converts |version| to a triplet of version numbers on behalf of |
+// MacOSXVersion(). Returns true on success. If |version| does not have the |
+// expected format, returns false. |version| must be in the form "10.9.2" or |
+// just "10.9". In the latter case, |bugfix| will be set to 0. |
+bool StringToVersionNumbers(const std::string& version, |
+ int* major, |
+ int* minor, |
+ int* bugfix) { |
+ size_t first_dot = version.find_first_of('.'); |
+ if (first_dot == 0 || first_dot == std::string::npos || |
+ first_dot == version.length() - 1) { |
+ LOG(ERROR) << "version has unexpected format"; |
+ return false; |
+ } |
+ if (!base::StringToInt(base::StringPiece(&version[0], first_dot), major)) { |
+ LOG(ERROR) << "version has unexpected format"; |
+ return false; |
+ } |
+ |
+ size_t second_dot = version.find_first_of('.', first_dot + 1); |
+ if (second_dot == version.length() - 1) { |
+ LOG(ERROR) << "version has unexpected format"; |
+ return false; |
+ } else if (second_dot == std::string::npos) { |
+ second_dot = version.length(); |
+ } |
+ |
+ if (!base::StringToInt(base::StringPiece(&version[first_dot + 1], |
+ second_dot - first_dot - 1), |
+ minor)) { |
+ LOG(ERROR) << "version has unexpected format"; |
+ return false; |
+ } |
+ |
+ if (second_dot == version.length()) { |
+ *bugfix = 0; |
+ } else if (!base::StringToInt( |
+ base::StringPiece(&version[second_dot + 1], |
+ version.length() - second_dot - 1), |
+ bugfix)) { |
+ LOG(ERROR) << "version has unexpected format"; |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+std::string IORegistryEntryDataPropertyAsString(io_registry_entry_t entry, |
+ CFStringRef key) { |
+ base::ScopedCFTypeRef<CFTypeRef> property( |
+ IORegistryEntryCreateCFProperty(entry, key, kCFAllocatorDefault, 0)); |
+ CFDataRef data = base::mac::CFCast<CFDataRef>(property); |
+ if (data && CFDataGetLength(data) > 0) { |
+ return reinterpret_cast<const char*>(CFDataGetBytePtr(data)); |
+ } |
+ |
+ return std::string(); |
+} |
+ |
+} // namespace |
+ |
+namespace crashpad { |
+ |
+int MacOSXMinorVersion() { |
+ // The Darwin major version is always 4 greater than the Mac OS X minor |
+ // version for Darwin versions beginning with 6, corresponding to Mac OS X |
+ // 10.2. |
+ static int mac_os_x_minor_version = DarwinMajorVersion() - 4; |
+ DCHECK(mac_os_x_minor_version >= 2); |
+ return mac_os_x_minor_version; |
+} |
+ |
+bool MacOSXVersion(int* major, |
+ int* minor, |
+ int* bugfix, |
+ std::string* build, |
+ bool* server, |
+ std::string* version_string) { |
+ base::ScopedCFTypeRef<CFDictionaryRef> dictionary( |
+ TryCFCopyServerVersionDictionary()); |
+ if (dictionary) { |
+ *server = true; |
+ } else { |
+ dictionary.reset(TryCFCopySystemVersionDictionary()); |
+ if (!dictionary) { |
+ LOG(ERROR) << "_CFCopySystemVersionDictionary failed"; |
+ return false; |
+ } |
+ *server = false; |
+ } |
+ |
+ bool success = true; |
+ |
+ CFStringRef version_cf = base::mac::CFCast<CFStringRef>( |
+ TryCFDictionaryGetValue(dictionary, _kCFSystemVersionProductVersionKey)); |
+ std::string version; |
+ if (!version_cf) { |
+ LOG(ERROR) << "version_cf not found"; |
+ success = false; |
+ } else { |
+ version = base::SysCFStringRefToUTF8(version_cf); |
+ success &= StringToVersionNumbers(version, major, minor, bugfix); |
+ } |
+ |
+ CFStringRef build_cf = base::mac::CFCast<CFStringRef>( |
+ TryCFDictionaryGetValue(dictionary, _kCFSystemVersionBuildVersionKey)); |
+ if (!build_cf) { |
+ LOG(ERROR) << "build_cf not found"; |
+ success = false; |
+ } else { |
+ build->assign(base::SysCFStringRefToUTF8(build_cf)); |
+ } |
+ |
+ CFStringRef product_cf = base::mac::CFCast<CFStringRef>( |
+ TryCFDictionaryGetValue(dictionary, _kCFSystemVersionProductNameKey)); |
+ std::string product; |
+ if (!product_cf) { |
+ LOG(ERROR) << "product_cf not found"; |
+ success = false; |
+ } else { |
+ product = base::SysCFStringRefToUTF8(product_cf); |
+ } |
+ |
+ // This key is not required, and in fact is normally not present. |
+ CFStringRef extra_cf = base::mac::CFCast<CFStringRef>(TryCFDictionaryGetValue( |
+ dictionary, _kCFSystemVersionProductVersionExtraKey)); |
+ std::string extra; |
+ if (extra_cf) { |
+ extra = base::SysCFStringRefToUTF8(extra_cf); |
+ } |
+ |
+ if (!product.empty() || !version.empty() || !build->empty()) { |
+ if (!extra.empty()) { |
+ version_string->assign(base::StringPrintf("%s %s %s (%s)", |
+ product.c_str(), |
+ version.c_str(), |
+ extra.c_str(), |
+ build->c_str())); |
+ } else { |
+ version_string->assign(base::StringPrintf( |
+ "%s %s (%s)", product.c_str(), version.c_str(), build->c_str())); |
+ } |
+ } |
+ |
+ return success; |
+} |
+ |
+void MacModelAndBoard(std::string* model, std::string* board_id) { |
+ base::mac::ScopedIOObject<io_service_t> platform_expert( |
+ IOServiceGetMatchingService(kIOMasterPortDefault, |
+ IOServiceMatching("IOPlatformExpertDevice"))); |
+ if (platform_expert) { |
+ model->assign( |
+ IORegistryEntryDataPropertyAsString(platform_expert, CFSTR("model"))); |
+ board_id->assign(IORegistryEntryDataPropertyAsString(platform_expert, |
+ CFSTR("board-id"))); |
+ } else { |
+ model->clear(); |
+ board_id->clear(); |
+ } |
+} |
+ |
+} // namespace crashpad |