| 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
|
|
|