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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #import "client/settings_mac.h"
16
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <uuid/uuid.h>
20
21 #include "base/logging.h"
22 #include "base/mac/scoped_nsautorelease_pool.h"
23 #include "base/mac/scoped_nsobject.h"
24 #include "base/posix/eintr_wrapper.h"
25 #include "base/strings/sys_string_conversions.h"
26 #include "util/misc/uuid.h"
27
28 namespace crashpad {
29 namespace internal {
30
31 namespace {
32
33 NSString* const kClientID = @"ClientID";
34 NSString* const kUploadsEnabled = @"UploadsEnabled";
35 NSString* const kLastUploadAttemptTime = @"LastUploadAttemptTime";
36
37 } // namespace
38
39 SettingsMac::SettingsMac(const base::FilePath& file_path)
40 : file_path_(file_path) {
41 }
42
43 SettingsMac::~SettingsMac() {
44 }
45
46 bool SettingsMac::Initialize() {
47 base::mac::ScopedNSAutoreleasePool pool;
48
49 ScopedFileHandle fd(HANDLE_EINTR(
50 open(file_path(),
51 O_CREAT | O_EXCL | O_WRONLY | O_EXLOCK,
52 0644)));
53
54 // The file was created, so this is a new database that needs to be
55 // initialized with a client ID.
56 if (fd.is_valid()) {
57 return GenerateClientID(fd.get());
58 }
59
60 // 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
61 fd.reset(HANDLE_EINTR(open(file_path(), O_RDONLY | O_SHLOCK)));
62 PLOG_IF(ERROR, !fd.is_valid()) << "open " << file_path();
63 return fd.is_valid();
64 }
65
66 bool SettingsMac::GetClientID(std::string* client_id) {
67 base::mac::ScopedNSAutoreleasePool pool;
68
69 id value = GetSetting(kClientID, [NSString class]);
70 if (!value)
71 return false;
Mark Mentovai 2015/03/09 14:01:23 If the plist is good but doesn’t contain this key
72
73 *client_id = base::SysNSStringToUTF8(value);
74 return true;
75 }
76
77 bool SettingsMac::GetUploadsEnabled(bool* enabled) {
78 base::mac::ScopedNSAutoreleasePool pool;
79
80 id value = GetSetting(kUploadsEnabled, [NSValue class]);
81 if (!value) {
82 // Return the default value per the interface requirement.
83 *enabled = false;
84 return true;
85 }
86
87 *enabled = [value boolValue];
88 return true;
89 }
90
91 bool SettingsMac::SetUploadsEnabled(bool enabled) {
92 base::mac::ScopedNSAutoreleasePool pool;
93
94 return SetSetting(kUploadsEnabled, [NSNumber numberWithBool:enabled]);
95 }
96
97 bool SettingsMac::GetLastUploadAttemptTime(time_t* time) {
98 base::mac::ScopedNSAutoreleasePool pool;
99
100 id value = GetSetting(kLastUploadAttemptTime, [NSValue class]);
101 if (!value) {
102 // Return the default value per the interface requirement.
103 *time = 0;
104 return true;
105 }
106
107 *time = [value doubleValue];
108 return true;
109 }
110
111 bool SettingsMac::SetLastUploadAttemptTime(time_t time) {
112 base::mac::ScopedNSAutoreleasePool pool;
113
114 return SetSetting(kLastUploadAttemptTime, [NSNumber numberWithDouble:time]);
115 }
116
117 NSMutableDictionary* SettingsMac::ReadPlist(
118 FileHandle fd) {
119 base::scoped_nsobject<NSFileHandle> file_handle(
Mark Mentovai 2015/03/09 14:01:23 Rewind the fd to the beginning of the file first.
120 [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]);
121 NSData* data = nil;
122 @try {
123 data = [file_handle readDataToEndOfFile];
124 } @catch (NSException* exception) {
125 LOG(ERROR) << "Failed to read settings: " << [exception description];
126 return nil;
127 }
128 if (!data)
129 return nil;
130
131 NSError* error = nil;
132 NSPropertyListMutabilityOptions options = NSPropertyListMutableContainers;
133 NSMutableDictionary* plist =
134 [NSPropertyListSerialization propertyListWithData:data
135 options:options
136 format:nullptr
137 error:&error];
138 LOG_IF(ERROR, error != nil) << "Failed to deserialize settings: "
139 << [error description];
140 return plist;
141 }
142
143 bool SettingsMac::WritePlist(FileHandle fd, NSDictionary* plist) {
144 NSError* error = nil;
145 NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0;
146 NSData* data = [NSPropertyListSerialization dataWithPropertyList:plist
147 format:format
148 options:0
149 error:&error];
150 LOG_IF(ERROR, error != nil) << "Failed to serialize settings: "
151 << [error description];
152 if (!data)
153 return false;
154
155 if (HANDLE_EINTR(ftruncate(fd, 0)) != 0) {
156 PLOG(ERROR) << "ftruncate failed on settings file";
157 return false;
158 }
159
160 if (LoggingSeekFile(fd, 0, SEEK_SET) != 0) {
161 return false;
162 }
163
164 base::scoped_nsobject<NSFileHandle> file_handle(
165 [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]);
166 @try {
167 [file_handle writeData:data];
168 } @catch (NSException* exception) {
169 LOG(ERROR) << "Failed to write settings: " << [exception description];
170 return false;
171 }
172
173 return true;
174 }
175
176 id SettingsMac::GetSetting(NSString* key, Class value_class) {
177 ScopedFileHandle fd(HANDLE_EINTR(open(file_path(), O_RDONLY | O_SHLOCK)));
178 if (!fd.is_valid()) {
179 PLOG(ERROR) << "open for get setting";
180 return nil;
181 }
182
183 NSMutableDictionary* plist = ReadPlist(fd.get());
184 if (!plist) {
185 fd.reset(); // Recovery will reopen the file.
186 plist = RecoverPlist();
187 if (!plist)
188 return nil;
189 }
190
191 id value = [plist objectForKey:key];
192 if (value && ![value isKindOfClass:value_class]) {
193 LOG(ERROR) << [key UTF8String] << " is not a "
194 << [NSStringFromClass(value_class) UTF8String];
195 return nil;
196 }
197
198 return value;
199 }
200
201 bool SettingsMac::SetSetting(NSString* key, NSObject* value) {
202 ScopedFileHandle fd(HANDLE_EINTR(open(file_path(), O_RDWR | O_EXLOCK)));
203 if (!fd.is_valid()) {
204 PLOG(ERROR) << "open for set setting";
205 return false;
206 }
207
208 NSMutableDictionary* plist = ReadPlist(fd.get());
209 if (!plist) {
210 fd.reset(); // Recovery will reopen the file.
Mark Mentovai 2015/03/09 14:01:23 Kinda unnecessary to do the close-reopen-reread pa
211 plist = RecoverPlist();
212 if (!plist)
213 return false;
214 }
215
216 [plist setObject:value forKey:key];
217
218 return WritePlist(fd.get(), plist);
219 }
220
221 bool SettingsMac::GenerateClientID(FileHandle fd) {
222 uuid_t client_id;
223 uuid_generate(client_id);
224
225 std::string client_id_string(UUID(client_id).ToString());
226
227 NSDictionary* plist =
228 @{ kClientID : base::SysUTF8ToNSString(client_id_string) };
229
230 return WritePlist(fd, plist);
231 }
232
233 NSMutableDictionary* SettingsMac::RecoverPlist() {
234 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
235
236 ScopedFileHandle fd(HANDLE_EINTR(open(file_path(), O_RDWR | O_EXLOCK)));
237 if (!fd.is_valid()) {
238 PLOG(ERROR) << "open for recovery";
239 return nil;
240 }
241
242 NSMutableDictionary* plist = ReadPlist(fd.get());
243 id client_id = [plist objectForKey:kClientID];
244 if (![client_id isKindOfClass:[NSString class]]) {
245 LOG(ERROR) << [kClientID UTF8String]
246 << " is corrupt, recovering settings file";
247 if (!GenerateClientID(fd.get())) {
248 LOG(ERROR) << "Failed to recover plist";
249 return nil;
250 }
251
252 LOG(INFO) << "Recovery complete, re-reading file";
253
254 return ReadPlist(fd.get());
255 }
256
257 LOG(INFO) << "Settings file is OK, no recovery required";
258
259 // The settings file may have already been recovered.
260 return plist;
261 }
262
263 } // namespace internal
264 } // namespace crashpad
OLDNEW
« 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