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

Side by Side Diff: rlz/mac/lib/rlz_value_store_mac.mm

Issue 10642009: Add a regenerate button to regenerate the password in Windows. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Sync and Merge. Created 8 years, 5 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
« no previous file with comments | « rlz/mac/lib/rlz_value_store_mac.h ('k') | rlz/rlz.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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.
4
5 #include "rlz/mac/lib/rlz_value_store_mac.h"
6
7 #include "base/mac/foundation_util.h"
8 #include "base/file_path.h"
9 #include "base/logging.h"
10 #include "base/sys_string_conversions.h"
11 #include "rlz/lib/assert.h"
12 #include "rlz/lib/lib_values.h"
13 #include "rlz/lib/rlz_lib.h"
14
15 #import <Foundation/Foundation.h>
16 #include <pthread.h>
17
18 using base::mac::ObjCCast;
19
20 namespace rlz_lib {
21
22 // These are written to disk and should not be changed.
23 NSString* const kPingTimeKey = @"pingTime";
24 NSString* const kAccessPointKey = @"accessPoints";
25 NSString* const kProductEventKey = @"productEvents";
26 NSString* const kStatefulEventKey = @"statefulEvents";
27
28 namespace {
29
30 NSString* GetNSProductName(Product product) {
31 return base::SysUTF8ToNSString(GetProductName(product));
32 }
33
34 NSString* GetNSAccessPointName(AccessPoint p) {
35 return base::SysUTF8ToNSString(GetAccessPointName(p));
36 }
37
38 // Retrieves a subdictionary in |p| for key |k|, creating it if necessary.
39 // If the dictionary contains an object for |k| that is not a mutable
40 // dictionary, that object is replaced with an empty mutable dictinary.
41 NSMutableDictionary* GetOrCreateDict(
42 NSMutableDictionary* p, NSString* k) {
43 NSMutableDictionary* d = ObjCCast<NSMutableDictionary>([p objectForKey:k]);
44 if (!d) {
45 d = [NSMutableDictionary dictionaryWithCapacity:0];
46 [p setObject:d forKey:k];
47 }
48 return d;
49 }
50
51 } // namespace
52
53 RlzValueStoreMac::RlzValueStoreMac(NSMutableDictionary* dict,
54 NSString* plist_path)
55 : dict_([dict retain]), plist_path_([plist_path retain]) {
56 }
57
58 RlzValueStoreMac::~RlzValueStoreMac() {
59 }
60
61 bool RlzValueStoreMac::HasAccess(AccessType type) {
62 NSFileManager* manager = [NSFileManager defaultManager];
63 switch (type) {
64 case kReadAccess: return [manager isReadableFileAtPath:plist_path_];
65 case kWriteAccess: return [manager isWritableFileAtPath:plist_path_];
66 }
67 }
68
69 bool RlzValueStoreMac::WritePingTime(Product product, int64 time) {
70 NSNumber* n = [NSNumber numberWithLongLong:time];
71 [ProductDict(product) setObject:n forKey:kPingTimeKey];
72 return true;
73 }
74
75 bool RlzValueStoreMac::ReadPingTime(Product product, int64* time) {
76 if (NSNumber* n =
77 ObjCCast<NSNumber>([ProductDict(product) objectForKey:kPingTimeKey])) {
78 *time = [n longLongValue];
79 return true;
80 }
81 return false;
82 }
83
84 bool RlzValueStoreMac::ClearPingTime(Product product) {
85 [ProductDict(product) removeObjectForKey:kPingTimeKey];
86 return true;
87 }
88
89
90 bool RlzValueStoreMac::WriteAccessPointRlz(AccessPoint access_point,
91 const char* new_rlz) {
92 NSMutableDictionary* d = GetOrCreateDict(WorkingDict(), kAccessPointKey);
93 [d setObject:base::SysUTF8ToNSString(new_rlz)
94 forKey:GetNSAccessPointName(access_point)];
95 return true;
96 }
97
98 bool RlzValueStoreMac::ReadAccessPointRlz(AccessPoint access_point,
99 char* rlz,
100 size_t rlz_size) {
101 // Reading a non-existent access point counts as success.
102 if (NSDictionary* d = ObjCCast<NSDictionary>(
103 [WorkingDict() objectForKey:kAccessPointKey])) {
104 NSString* val = ObjCCast<NSString>(
105 [d objectForKey:GetNSAccessPointName(access_point)]);
106 if (!val) {
107 if (rlz_size > 0)
108 rlz[0] = '\0';
109 return true;
110 }
111
112 std::string s = base::SysNSStringToUTF8(val);
113 if (s.size() >= rlz_size) {
114 rlz[0] = 0;
115 ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size");
116 return false;
117 }
118 strncpy(rlz, s.c_str(), rlz_size);
119 return true;
120 }
121 if (rlz_size > 0)
122 rlz[0] = '\0';
123 return true;
124 }
125
126 bool RlzValueStoreMac::ClearAccessPointRlz(AccessPoint access_point) {
127 if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>(
128 [WorkingDict() objectForKey:kAccessPointKey])) {
129 [d removeObjectForKey:GetNSAccessPointName(access_point)];
130 }
131 return true;
132 }
133
134
135 bool RlzValueStoreMac::AddProductEvent(Product product,
136 const char* event_rlz) {
137 [GetOrCreateDict(ProductDict(product), kProductEventKey)
138 setObject:[NSNumber numberWithBool:YES]
139 forKey:base::SysUTF8ToNSString(event_rlz)];
140 return true;
141 }
142
143 bool RlzValueStoreMac::ReadProductEvents(Product product,
144 std::vector<std::string>* events) {
145 if (NSDictionary* d = ObjCCast<NSDictionary>(
146 [ProductDict(product) objectForKey:kProductEventKey])) {
147 for (NSString* s in d)
148 events->push_back(base::SysNSStringToUTF8(s));
149 return true;
150 }
151 return true;
152 }
153
154 bool RlzValueStoreMac::ClearProductEvent(Product product,
155 const char* event_rlz) {
156 if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>(
157 [ProductDict(product) objectForKey:kProductEventKey])) {
158 [d removeObjectForKey:base::SysUTF8ToNSString(event_rlz)];
159 return true;
160 }
161 return false;
162 }
163
164 bool RlzValueStoreMac::ClearAllProductEvents(Product product) {
165 [ProductDict(product) removeObjectForKey:kProductEventKey];
166 return true;
167 }
168
169
170 bool RlzValueStoreMac::AddStatefulEvent(Product product,
171 const char* event_rlz) {
172 [GetOrCreateDict(ProductDict(product), kStatefulEventKey)
173 setObject:[NSNumber numberWithBool:YES]
174 forKey:base::SysUTF8ToNSString(event_rlz)];
175 return true;
176 }
177
178 bool RlzValueStoreMac::IsStatefulEvent(Product product,
179 const char* event_rlz) {
180 if (NSDictionary* d = ObjCCast<NSDictionary>(
181 [ProductDict(product) objectForKey:kStatefulEventKey])) {
182 return [d objectForKey:base::SysUTF8ToNSString(event_rlz)] != nil;
183 }
184 return false;
185 }
186
187 bool RlzValueStoreMac::ClearAllStatefulEvents(Product product) {
188 [ProductDict(product) removeObjectForKey:kStatefulEventKey];
189 return true;
190 }
191
192
193 void RlzValueStoreMac::CollectGarbage() {
194 NOTIMPLEMENTED();
195 }
196
197 NSDictionary* RlzValueStoreMac::dictionary() {
198 return dict_.get();
199 }
200
201 NSMutableDictionary* RlzValueStoreMac::WorkingDict() {
202 std::string brand(SupplementaryBranding::GetBrand());
203 if (brand.empty())
204 return dict_;
205
206 NSString* brand_ns =
207 [@"brand_" stringByAppendingString:base::SysUTF8ToNSString(brand)];
208
209 return GetOrCreateDict(dict_.get(), brand_ns);
210 }
211
212 NSMutableDictionary* RlzValueStoreMac::ProductDict(Product p) {
213 return GetOrCreateDict(WorkingDict(), GetNSProductName(p));
214 }
215
216
217 namespace {
218
219 // Creating a recursive cross-process mutex on windows is one line. On mac,
220 // there's no primitve for that, so this lock is emulated by an in-process
221 // mutex to get the recursive part, followed by a cross-process lock for the
222 // cross-process part.
223
224 // This is a struct so that it doesn't need a static initializer.
225 struct RecursiveCrossProcessLock {
226 // Tries to acquire a recursive cross-process lock. Note that this _always_
227 // acquires the in-process lock (if it wasn't already acquired). The parent
228 // directory of |lock_file| must exist.
229 bool TryGetCrossProcessLock(NSString* lock_filename);
230
231 // Releases the lock. Should always be called, even if
232 // TryGetCrossProcessLock() returns false.
233 void ReleaseLock();
234
235 pthread_mutex_t recursive_lock_;
236 pthread_t locking_thread_;
237
238 NSDistributedLock* file_lock_;
239 } g_recursive_lock = {
240 // PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and is buggy
241 // on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), so emulate
242 // recursive locking with a normal non-recursive mutex.
243 PTHREAD_MUTEX_INITIALIZER
244 };
245
246 bool RecursiveCrossProcessLock::TryGetCrossProcessLock(
247 NSString* lock_filename) {
248 bool just_got_lock = false;
249
250 // Emulate a recursive mutex with a non-recursive one.
251 if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) {
252 if (pthread_equal(pthread_self(), locking_thread_) == 0) {
253 // Some other thread has the lock, wait for it.
254 pthread_mutex_lock(&recursive_lock_);
255 CHECK(locking_thread_ == 0);
256 just_got_lock = true;
257 }
258 } else {
259 just_got_lock = true;
260 }
261
262 locking_thread_ = pthread_self();
263
264 // Try to acquire file lock.
265 if (just_got_lock) {
266 const int kMaxTimeoutMS = 5000; // Matches windows.
267 const int kSleepPerTryMS = 200;
268
269 CHECK(!file_lock_);
270 file_lock_ = [[NSDistributedLock alloc] initWithPath:lock_filename];
271
272 BOOL got_file_lock = NO;
273 int elapsedMS = 0;
274 while (!(got_file_lock = [file_lock_ tryLock]) &&
275 elapsedMS < kMaxTimeoutMS) {
276 usleep(kSleepPerTryMS * 1000);
277 elapsedMS += kSleepPerTryMS;
278 }
279
280 if (!got_file_lock) {
281 [file_lock_ release];
282 file_lock_ = nil;
283 return false;
284 }
285 return true;
286 } else {
287 return file_lock_ != nil;
288 }
289 }
290
291 void RecursiveCrossProcessLock::ReleaseLock() {
292 if (file_lock_) {
293 [file_lock_ unlock];
294 [file_lock_ release];
295 file_lock_ = nil;
296 }
297
298 locking_thread_ = 0;
299 pthread_mutex_unlock(&recursive_lock_);
300 }
301
302
303 // This is set during test execution, to write RLZ files into a temporary
304 // directory instead of the user's Application Support folder.
305 NSString* g_test_folder;
306
307 // RlzValueStoreMac keeps its data in memory and only writes it to disk when
308 // ScopedRlzValueStoreLock goes out of scope. Hence, if several
309 // ScopedRlzValueStoreLocks are nested, they all need to use the same store
310 // object.
311
312 // This counts the nesting depth.
313 int g_lock_depth = 0;
314
315 // This is the store object that might be shared. Only set if g_lock_depth > 0.
316 RlzValueStoreMac* g_store_object = NULL;
317
318
319 NSString* CreateRlzDirectory() {
320 NSFileManager* manager = [NSFileManager defaultManager];
321 NSArray* paths = NSSearchPathForDirectoriesInDomains(
322 NSApplicationSupportDirectory, NSUserDomainMask, /*expandTilde=*/YES);
323 NSString* folder = nil;
324 if ([paths count] > 0)
325 folder = ObjCCast<NSString>([paths objectAtIndex:0]);
326 if (!folder)
327 folder = [@"~/Library/Application Support" stringByStandardizingPath];
328 folder = [folder stringByAppendingPathComponent:@"Google/RLZ"];
329
330 if (g_test_folder)
331 folder = [g_test_folder stringByAppendingPathComponent:folder];
332
333 [manager createDirectoryAtPath:folder
334 withIntermediateDirectories:YES
335 attributes:nil
336 error:nil];
337 return folder;
338 }
339
340 // Returns the path of the rlz plist store, also creates the parent directory
341 // path if it doesn't exist.
342 NSString* RlzPlistFilename() {
343 NSString* const kRlzFile = @"RlzStore.plist";
344 return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile];
345 }
346
347 // Returns the path of the rlz lock file, also creates the parent directory
348 // path if it doesn't exist.
349 NSString* RlzLockFilename() {
350 NSString* const kRlzFile = @"lockfile";
351 return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile];
352 }
353
354 } // namespace
355
356 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
357 bool got_distributed_lock =
358 g_recursive_lock.TryGetCrossProcessLock(RlzLockFilename());
359 // At this point, we hold the in-process lock, no matter the value of
360 // |got_distributed_lock|.
361
362 ++g_lock_depth;
363
364 if (!got_distributed_lock) {
365 // Give up. |store_| isn't set, which signals to callers that acquiring
366 // the lock failed. |g_recursive_lock| will be released by the
367 // destructor.
368 CHECK(!g_store_object);
369 return;
370 }
371
372 if (g_lock_depth > 1) {
373 // Reuse the already existing store object.
374 CHECK(g_store_object);
375 store_.reset(g_store_object);
376 return;
377 }
378
379 CHECK(!g_store_object);
380
381 NSString* plist = RlzPlistFilename();
382
383 // Create an empty file if none exists yet.
384 NSFileManager* manager = [NSFileManager defaultManager];
385 if (![manager fileExistsAtPath:plist isDirectory:NULL])
386 [[NSDictionary dictionary] writeToFile:plist atomically:YES];
387
388 NSMutableDictionary* dict =
389 [NSMutableDictionary dictionaryWithContentsOfFile:plist];
390 VERIFY(dict);
391
392 if (dict) {
393 store_.reset(new RlzValueStoreMac(dict, plist));
394 g_store_object = (RlzValueStoreMac*)store_.get();
395 }
396 }
397
398 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
399 --g_lock_depth;
400 CHECK(g_lock_depth >= 0);
401
402 if (g_lock_depth > 0) {
403 // Other locks are still using store_, don't free it yet.
404 ignore_result(store_.release());
405 return;
406 }
407
408 if (store_.get()) {
409 g_store_object = NULL;
410
411 NSDictionary* dict =
412 static_cast<RlzValueStoreMac*>(store_.get())->dictionary();
413 VERIFY([dict writeToFile:RlzPlistFilename() atomically:YES]);
414 }
415
416 // Check that "store_ set" => "file_lock acquired". The converse isn't true,
417 // for example if the rlz data file can't be read.
418 if (store_.get())
419 CHECK(g_recursive_lock.file_lock_);
420 if (!g_recursive_lock.file_lock_)
421 CHECK(!store_.get());
422
423 g_recursive_lock.ReleaseLock();
424 }
425
426 RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
427 return store_.get();
428 }
429
430 namespace testing {
431
432 void SetRlzStoreDirectory(const FilePath& directory) {
433 base::mac::ScopedNSAutoreleasePool pool;
434
435 [g_test_folder release];
436 if (directory.empty()) {
437 g_test_folder = nil;
438 } else {
439 // Not Unsafe on OS X.
440 g_test_folder =
441 [[NSString alloc] initWithUTF8String:directory.AsUTF8Unsafe().c_str()];
442 }
443 }
444
445 } // namespace testing
446
447 } // namespace rlz_lib
OLDNEW
« no previous file with comments | « rlz/mac/lib/rlz_value_store_mac.h ('k') | rlz/rlz.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698