Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(17)

Unified Diff: client/settings_mac.mm

Issue 988063003: Define the Settings interface for a CrashReportDatabase and provide a Mac implementation. (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address comments Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: client/settings_mac.mm
diff --git a/client/settings_mac.mm b/client/settings_mac.mm
new file mode 100644
index 0000000000000000000000000000000000000000..840001268362ea1f4b7857a093b3a458691d8746
--- /dev/null
+++ b/client/settings_mac.mm
@@ -0,0 +1,264 @@
+// Copyright 2015 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.
+
+#import "client/settings_mac.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/sys_string_conversions.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+namespace internal {
+
+namespace {
+
+NSString* const kClientID = @"ClientID";
+NSString* const kUploadsEnabled = @"UploadsEnabled";
+NSString* const kLastUploadAttemptTime = @"LastUploadAttemptTime";
+
+} // namespace
+
+SettingsMac::SettingsMac(const base::FilePath& file_path)
+ : file_path_(file_path) {
+}
+
+SettingsMac::~SettingsMac() {
+}
+
+bool SettingsMac::Initialize() {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ ScopedFileHandle fd(HANDLE_EINTR(
+ open(file_path(),
+ O_CREAT | O_EXCL | O_WRONLY | O_EXLOCK,
+ 0644)));
+
+ // The file was created, so this is a new database that needs to be
+ // initialized with a client ID.
+ if (fd.is_valid()) {
+ return GenerateClientID(fd.get());
+ }
+
+ // The file wasn't opened successfully, try opening it without creating.
Mark Mentovai 2015/03/09 14:01:23 “Wasn’t opened successfully” glosses over the fact
+ fd.reset(HANDLE_EINTR(open(file_path(), O_RDONLY | O_SHLOCK)));
+ PLOG_IF(ERROR, !fd.is_valid()) << "open " << file_path();
+ return fd.is_valid();
+}
+
+bool SettingsMac::GetClientID(std::string* client_id) {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ id value = GetSetting(kClientID, [NSString class]);
+ if (!value)
+ return false;
Mark Mentovai 2015/03/09 14:01:23 If the plist is good but doesn’t contain this key
+
+ *client_id = base::SysNSStringToUTF8(value);
+ return true;
+}
+
+bool SettingsMac::GetUploadsEnabled(bool* enabled) {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ id value = GetSetting(kUploadsEnabled, [NSValue class]);
+ if (!value) {
+ // Return the default value per the interface requirement.
+ *enabled = false;
+ return true;
+ }
+
+ *enabled = [value boolValue];
+ return true;
+}
+
+bool SettingsMac::SetUploadsEnabled(bool enabled) {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ return SetSetting(kUploadsEnabled, [NSNumber numberWithBool:enabled]);
+}
+
+bool SettingsMac::GetLastUploadAttemptTime(time_t* time) {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ id value = GetSetting(kLastUploadAttemptTime, [NSValue class]);
+ if (!value) {
+ // Return the default value per the interface requirement.
+ *time = 0;
+ return true;
+ }
+
+ *time = [value doubleValue];
+ return true;
+}
+
+bool SettingsMac::SetLastUploadAttemptTime(time_t time) {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ return SetSetting(kLastUploadAttemptTime, [NSNumber numberWithDouble:time]);
+}
+
+NSMutableDictionary* SettingsMac::ReadPlist(
+ FileHandle fd) {
+ base::scoped_nsobject<NSFileHandle> file_handle(
Mark Mentovai 2015/03/09 14:01:23 Rewind the fd to the beginning of the file first.
+ [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]);
+ NSData* data = nil;
+ @try {
+ data = [file_handle readDataToEndOfFile];
+ } @catch (NSException* exception) {
+ LOG(ERROR) << "Failed to read settings: " << [exception description];
+ return nil;
+ }
+ if (!data)
+ return nil;
+
+ NSError* error = nil;
+ NSPropertyListMutabilityOptions options = NSPropertyListMutableContainers;
+ NSMutableDictionary* plist =
+ [NSPropertyListSerialization propertyListWithData:data
+ options:options
+ format:nullptr
+ error:&error];
+ LOG_IF(ERROR, error != nil) << "Failed to deserialize settings: "
+ << [error description];
+ return plist;
+}
+
+bool SettingsMac::WritePlist(FileHandle fd, NSDictionary* plist) {
+ NSError* error = nil;
+ NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0;
+ NSData* data = [NSPropertyListSerialization dataWithPropertyList:plist
+ format:format
+ options:0
+ error:&error];
+ LOG_IF(ERROR, error != nil) << "Failed to serialize settings: "
+ << [error description];
+ if (!data)
+ return false;
+
+ if (HANDLE_EINTR(ftruncate(fd, 0)) != 0) {
+ PLOG(ERROR) << "ftruncate failed on settings file";
+ return false;
+ }
+
+ if (LoggingSeekFile(fd, 0, SEEK_SET) != 0) {
+ return false;
+ }
+
+ base::scoped_nsobject<NSFileHandle> file_handle(
+ [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]);
+ @try {
+ [file_handle writeData:data];
+ } @catch (NSException* exception) {
+ LOG(ERROR) << "Failed to write settings: " << [exception description];
+ return false;
+ }
+
+ return true;
+}
+
+id SettingsMac::GetSetting(NSString* key, Class value_class) {
+ ScopedFileHandle fd(HANDLE_EINTR(open(file_path(), O_RDONLY | O_SHLOCK)));
+ if (!fd.is_valid()) {
+ PLOG(ERROR) << "open for get setting";
+ return nil;
+ }
+
+ NSMutableDictionary* plist = ReadPlist(fd.get());
+ if (!plist) {
+ fd.reset(); // Recovery will reopen the file.
+ plist = RecoverPlist();
+ if (!plist)
+ return nil;
+ }
+
+ id value = [plist objectForKey:key];
+ if (value && ![value isKindOfClass:value_class]) {
+ LOG(ERROR) << [key UTF8String] << " is not a "
+ << [NSStringFromClass(value_class) UTF8String];
+ return nil;
+ }
+
+ return value;
+}
+
+bool SettingsMac::SetSetting(NSString* key, NSObject* value) {
+ ScopedFileHandle fd(HANDLE_EINTR(open(file_path(), O_RDWR | O_EXLOCK)));
+ if (!fd.is_valid()) {
+ PLOG(ERROR) << "open for set setting";
+ return false;
+ }
+
+ NSMutableDictionary* plist = ReadPlist(fd.get());
+ if (!plist) {
+ fd.reset(); // Recovery will reopen the file.
Mark Mentovai 2015/03/09 14:01:23 Kinda unnecessary to do the close-reopen-reread pa
+ plist = RecoverPlist();
+ if (!plist)
+ return false;
+ }
+
+ [plist setObject:value forKey:key];
+
+ return WritePlist(fd.get(), plist);
+}
+
+bool SettingsMac::GenerateClientID(FileHandle fd) {
+ uuid_t client_id;
+ uuid_generate(client_id);
+
+ std::string client_id_string(UUID(client_id).ToString());
+
+ NSDictionary* plist =
+ @{ kClientID : base::SysUTF8ToNSString(client_id_string) };
+
+ return WritePlist(fd, plist);
+}
+
+NSMutableDictionary* SettingsMac::RecoverPlist() {
+ LOG(INFO) << "Attempting to recover settings file " << file_path();
Mark Mentovai 2015/03/09 14:01:23 I don’t know if we need the LOG(INFO)s. Maybe just
+
+ ScopedFileHandle fd(HANDLE_EINTR(open(file_path(), O_RDWR | O_EXLOCK)));
+ if (!fd.is_valid()) {
+ PLOG(ERROR) << "open for recovery";
+ return nil;
+ }
+
+ NSMutableDictionary* plist = ReadPlist(fd.get());
+ id client_id = [plist objectForKey:kClientID];
+ if (![client_id isKindOfClass:[NSString class]]) {
+ LOG(ERROR) << [kClientID UTF8String]
+ << " is corrupt, recovering settings file";
+ if (!GenerateClientID(fd.get())) {
+ LOG(ERROR) << "Failed to recover plist";
+ return nil;
+ }
+
+ LOG(INFO) << "Recovery complete, re-reading file";
+
+ return ReadPlist(fd.get());
+ }
+
+ LOG(INFO) << "Settings file is OK, no recovery required";
+
+ // The settings file may have already been recovered.
+ return plist;
+}
+
+} // namespace internal
+} // namespace crashpad
« client/settings_mac.h ('K') | « client/settings_mac.h ('k') | client/settings_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698