| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import <Foundation/Foundation.h> | 5 #import <Foundation/Foundation.h> |
| 6 #include <launch.h> | 6 #include <launch.h> |
| 7 | 7 |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 #include "base/strings/sys_string_conversions.h" | 23 #include "base/strings/sys_string_conversions.h" |
| 24 #include "base/threading/thread_restrictions.h" | 24 #include "base/threading/thread_restrictions.h" |
| 25 #include "base/version.h" | 25 #include "base/version.h" |
| 26 #include "chrome/common/chrome_paths.h" | 26 #include "chrome/common/chrome_paths.h" |
| 27 #include "chrome/common/chrome_switches.h" | 27 #include "chrome/common/chrome_switches.h" |
| 28 #include "chrome/common/mac/launchd.h" | 28 #include "chrome/common/mac/launchd.h" |
| 29 #include "chrome/common/service_process_util_posix.h" | 29 #include "chrome/common/service_process_util_posix.h" |
| 30 #include "components/version_info/version_info.h" | 30 #include "components/version_info/version_info.h" |
| 31 #include "ipc/unix_domain_socket_util.h" | 31 #include "ipc/unix_domain_socket_util.h" |
| 32 | 32 |
| 33 @interface NSFileManager (YosemiteSDK) |
| 34 - (BOOL)getRelationship:(NSURLRelationship*)outRelationship |
| 35 ofDirectory:(NSSearchPathDirectory)directory |
| 36 inDomain:(NSSearchPathDomainMask)domainMask |
| 37 toItemAtURL:(NSURL*)url |
| 38 error:(NSError**)error; |
| 39 @end |
| 40 |
| 33 using ::base::FilePathWatcher; | 41 using ::base::FilePathWatcher; |
| 34 | 42 |
| 35 namespace { | 43 namespace { |
| 36 | 44 |
| 37 #define kServiceProcessSessionType "Aqua" | 45 #define kServiceProcessSessionType "Aqua" |
| 38 | 46 |
| 39 CFStringRef CopyServiceProcessLaunchDName() { | 47 CFStringRef CopyServiceProcessLaunchDName() { |
| 40 base::mac::ScopedNSAutoreleasePool pool; | 48 base::mac::ScopedNSAutoreleasePool pool; |
| 41 NSBundle* bundle = base::mac::FrameworkBundle(); | 49 NSBundle* bundle = base::mac::FrameworkBundle(); |
| 42 return CFStringCreateCopy(kCFAllocatorDefault, | 50 return CFStringCreateCopy(kCFAllocatorDefault, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 54 ns_path = [ns_path stringByReplacingOccurrencesOfString:@" " | 62 ns_path = [ns_path stringByReplacingOccurrencesOfString:@" " |
| 55 withString:@"_"]; | 63 withString:@"_"]; |
| 56 label = [label stringByAppendingString:ns_path]; | 64 label = [label stringByAppendingString:ns_path]; |
| 57 return label; | 65 return label; |
| 58 } | 66 } |
| 59 | 67 |
| 60 NSString* GetServiceProcessLaunchDSocketKey() { | 68 NSString* GetServiceProcessLaunchDSocketKey() { |
| 61 return @"ServiceProcessSocket"; | 69 return @"ServiceProcessSocket"; |
| 62 } | 70 } |
| 63 | 71 |
| 64 bool GetParentFSRef(const FSRef& child, FSRef* parent) { | |
| 65 return FSGetCatalogInfo(&child, 0, NULL, NULL, NULL, parent) == noErr; | |
| 66 } | |
| 67 | |
| 68 bool RemoveFromLaunchd() { | 72 bool RemoveFromLaunchd() { |
| 69 // We're killing a file. | 73 // We're killing a file. |
| 70 base::ThreadRestrictions::AssertIOAllowed(); | 74 base::ThreadRestrictions::AssertIOAllowed(); |
| 71 base::ScopedCFTypeRef<CFStringRef> name(CopyServiceProcessLaunchDName()); | 75 base::ScopedCFTypeRef<CFStringRef> name(CopyServiceProcessLaunchDName()); |
| 72 return Launchd::GetInstance()->DeletePlist(Launchd::User, | 76 return Launchd::GetInstance()->DeletePlist(Launchd::User, |
| 73 Launchd::Agent, | 77 Launchd::Agent, |
| 74 name); | 78 name); |
| 75 } | 79 } |
| 76 | 80 |
| 77 class ExecFilePathWatcherCallback { | 81 class ExecFilePathWatcherCallback { |
| 78 public: | 82 public: |
| 79 ExecFilePathWatcherCallback() {} | 83 ExecFilePathWatcherCallback() {} |
| 80 ~ExecFilePathWatcherCallback() {} | 84 ~ExecFilePathWatcherCallback() {} |
| 81 | 85 |
| 82 bool Init(const base::FilePath& path); | 86 bool Init(const base::FilePath& path); |
| 83 void NotifyPathChanged(const base::FilePath& path, bool error); | 87 void NotifyPathChanged(const base::FilePath& path, bool error); |
| 84 | 88 |
| 85 private: | 89 private: |
| 86 FSRef executable_fsref_; | 90 base::scoped_nsobject<NSURL> executable_fsref_; |
| 87 }; | 91 }; |
| 88 | 92 |
| 89 base::FilePath GetServiceProcessSocketName() { | 93 base::FilePath GetServiceProcessSocketName() { |
| 90 base::FilePath socket_name; | 94 base::FilePath socket_name; |
| 91 PathService::Get(base::DIR_TEMP, &socket_name); | 95 PathService::Get(base::DIR_TEMP, &socket_name); |
| 92 std::string pipe_name = GetServiceProcessScopedName("srv"); | 96 std::string pipe_name = GetServiceProcessScopedName("srv"); |
| 93 socket_name = socket_name.Append(pipe_name); | 97 socket_name = socket_name.Append(pipe_name); |
| 94 CHECK_LT(socket_name.value().size(), IPC::kMaxSocketNameLength); | 98 CHECK_LT(socket_name.value().size(), IPC::kMaxSocketNameLength); |
| 95 return socket_name; | 99 return socket_name; |
| 96 } | 100 } |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 false, | 321 false, |
| 318 base::Bind(&ExecFilePathWatcherCallback::NotifyPathChanged, | 322 base::Bind(&ExecFilePathWatcherCallback::NotifyPathChanged, |
| 319 base::Owned(callback.release())))) { | 323 base::Owned(callback.release())))) { |
| 320 DLOG(ERROR) << "executable_watcher.watch " << executable_path.value(); | 324 DLOG(ERROR) << "executable_watcher.watch " << executable_path.value(); |
| 321 return false; | 325 return false; |
| 322 } | 326 } |
| 323 return true; | 327 return true; |
| 324 } | 328 } |
| 325 | 329 |
| 326 bool ExecFilePathWatcherCallback::Init(const base::FilePath& path) { | 330 bool ExecFilePathWatcherCallback::Init(const base::FilePath& path) { |
| 327 return base::mac::FSRefFromPath(path.value(), &executable_fsref_); | 331 NSString* path_string = base::mac::FilePathToNSString(path); |
| 332 NSURL* path_url = [NSURL fileURLWithPath:path_string isDirectory:NO]; |
| 333 executable_fsref_.reset([[path_url fileReferenceURL] retain]); |
| 334 return executable_fsref_.get() != nil; |
| 328 } | 335 } |
| 329 | 336 |
| 330 void ExecFilePathWatcherCallback::NotifyPathChanged(const base::FilePath& path, | 337 void ExecFilePathWatcherCallback::NotifyPathChanged(const base::FilePath& path, |
| 331 bool error) { | 338 bool error) { |
| 332 if (error) { | 339 if (error) { |
| 333 NOTREACHED(); // TODO(darin): Do something smarter? | 340 NOTREACHED(); // TODO(darin): Do something smarter? |
| 334 return; | 341 return; |
| 335 } | 342 } |
| 336 | 343 |
| 337 base::mac::ScopedNSAutoreleasePool pool; | 344 base::mac::ScopedNSAutoreleasePool pool; |
| 338 bool needs_shutdown = false; | 345 bool needs_shutdown = false; |
| 339 bool needs_restart = false; | 346 bool needs_restart = false; |
| 340 bool good_bundle = false; | 347 bool good_bundle = false; |
| 341 | 348 |
| 342 FSRef macos_fsref; | 349 // Go from bundle/Contents/MacOS/executable to bundle. |
| 343 if (GetParentFSRef(executable_fsref_, &macos_fsref)) { | 350 NSURL* bundle_url = [[[executable_fsref_ URLByDeletingLastPathComponent] |
| 344 FSRef contents_fsref; | 351 URLByDeletingLastPathComponent] URLByDeletingLastPathComponent]; |
| 345 if (GetParentFSRef(macos_fsref, &contents_fsref)) { | 352 if (bundle_url) { |
| 346 FSRef bundle_fsref; | 353 base::ScopedCFTypeRef<CFBundleRef> bundle( |
| 347 if (GetParentFSRef(contents_fsref, &bundle_fsref)) { | 354 CFBundleCreate(kCFAllocatorDefault, base::mac::NSToCFCast(bundle_url))); |
| 348 base::ScopedCFTypeRef<CFURLRef> bundle_url( | 355 good_bundle = CFBundleGetIdentifier(bundle) != NULL; |
| 349 CFURLCreateFromFSRef(kCFAllocatorDefault, &bundle_fsref)); | |
| 350 if (bundle_url.get()) { | |
| 351 base::ScopedCFTypeRef<CFBundleRef> bundle( | |
| 352 CFBundleCreate(kCFAllocatorDefault, bundle_url)); | |
| 353 // Check to see if the bundle still has a minimal structure. | |
| 354 good_bundle = CFBundleGetIdentifier(bundle) != NULL; | |
| 355 } | |
| 356 } | |
| 357 } | |
| 358 } | 356 } |
| 357 |
| 359 if (!good_bundle) { | 358 if (!good_bundle) { |
| 360 needs_shutdown = true; | 359 needs_shutdown = true; |
| 361 } else { | 360 } else { |
| 362 Boolean in_trash; | 361 bool in_trash = false; |
| 363 OSErr err = FSDetermineIfRefIsEnclosedByFolder(kOnAppropriateDisk, | 362 NSFileManager* file_manager = [NSFileManager defaultManager]; |
| 364 kTrashFolderType, | 363 // Apple deprecated FSDetermineIfRefIsEnclosedByFolder() when deploying to |
| 365 &executable_fsref_, | 364 // 10.8, but didn't add getRelationship:... until 10.10. So fall back to |
| 366 &in_trash); | 365 // the deprecated function while running on 10.9 (and delete the else block |
| 367 if (err == noErr && in_trash) { | 366 // when Chromium requires OS X 10.10+). |
| 367 if ([file_manager respondsToSelector:@selector(getRelationship: |
| 368 ofDirectory: |
| 369 inDomain: |
| 370 toItemAtURL: |
| 371 error:)]) { |
| 372 NSURLRelationship relationship; |
| 373 if ([file_manager getRelationship:&relationship |
| 374 ofDirectory:NSTrashDirectory |
| 375 inDomain:0 |
| 376 toItemAtURL:executable_fsref_ |
| 377 error:nil]) { |
| 378 in_trash = relationship == NSURLRelationshipContains; |
| 379 } |
| 380 } else { |
| 381 DCHECK(base::mac::IsAtMostOS10_9()); |
| 382 Boolean fs_in_trash; |
| 383 FSRef ref; |
| 384 if (CFURLGetFSRef(base::mac::NSToCFCast(executable_fsref_.get()), &ref)) { |
| 385 #pragma clang diagnostic push |
| 386 #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| 387 // This is ok because it only happens on 10.9 and won't be needed once |
| 388 // we stop supporting that. |
| 389 OSErr err = FSDetermineIfRefIsEnclosedByFolder( |
| 390 kOnAppropriateDisk, kTrashFolderType, &ref, &fs_in_trash); |
| 391 #pragma clang diagnostic pop |
| 392 if (err == noErr && fs_in_trash) |
| 393 in_trash = true; |
| 394 } |
| 395 } |
| 396 if (in_trash) { |
| 368 needs_shutdown = true; | 397 needs_shutdown = true; |
| 369 } else { | 398 } else { |
| 370 bool was_moved = true; | 399 bool was_moved = true; |
| 371 FSRef path_ref; | 400 NSString* path_string = base::mac::FilePathToNSString(path); |
| 372 if (base::mac::FSRefFromPath(path.value(), &path_ref)) { | 401 NSURL* path_url = [NSURL fileURLWithPath:path_string isDirectory:NO]; |
| 373 if (FSCompareFSRefs(&path_ref, &executable_fsref_) == noErr) { | 402 NSURL* path_ref = [path_url fileReferenceURL]; |
| 403 if (path_ref != nil) { |
| 404 if ([path_ref isEqual:executable_fsref_]) { |
| 374 was_moved = false; | 405 was_moved = false; |
| 375 } | 406 } |
| 376 } | 407 } |
| 377 if (was_moved) { | 408 if (was_moved) { |
| 378 needs_restart = true; | 409 needs_restart = true; |
| 379 } | 410 } |
| 380 } | 411 } |
| 381 } | 412 } |
| 382 if (needs_shutdown || needs_restart) { | 413 if (needs_shutdown || needs_restart) { |
| 383 // First deal with the plist. | 414 // First deal with the plist. |
| 384 base::ScopedCFTypeRef<CFStringRef> name(CopyServiceProcessLaunchDName()); | 415 base::ScopedCFTypeRef<CFStringRef> name(CopyServiceProcessLaunchDName()); |
| 385 if (needs_restart) { | 416 if (needs_restart) { |
| 386 base::ScopedCFTypeRef<CFMutableDictionaryRef> plist( | 417 base::ScopedCFTypeRef<CFMutableDictionaryRef> plist( |
| 387 Launchd::GetInstance()->CreatePlistFromFile( | 418 Launchd::GetInstance()->CreatePlistFromFile( |
| 388 Launchd::User, Launchd::Agent, name)); | 419 Launchd::User, Launchd::Agent, name)); |
| 389 if (plist.get()) { | 420 if (plist.get()) { |
| 390 NSMutableDictionary* ns_plist = base::mac::CFToNSCast(plist); | 421 NSMutableDictionary* ns_plist = base::mac::CFToNSCast(plist); |
| 391 std::string new_path = base::mac::PathFromFSRef(executable_fsref_); | 422 NSURL* new_path = [executable_fsref_ filePathURL]; |
| 392 NSString* ns_new_path = base::SysUTF8ToNSString(new_path); | 423 DCHECK([new_path isFileURL]); |
| 424 NSString* ns_new_path = [new_path path]; |
| 393 [ns_plist setObject:ns_new_path forKey:@ LAUNCH_JOBKEY_PROGRAM]; | 425 [ns_plist setObject:ns_new_path forKey:@ LAUNCH_JOBKEY_PROGRAM]; |
| 394 base::scoped_nsobject<NSMutableArray> args([[ns_plist | 426 base::scoped_nsobject<NSMutableArray> args([[ns_plist |
| 395 objectForKey:@LAUNCH_JOBKEY_PROGRAMARGUMENTS] mutableCopy]); | 427 objectForKey:@LAUNCH_JOBKEY_PROGRAMARGUMENTS] mutableCopy]); |
| 396 [args replaceObjectAtIndex:0 withObject:ns_new_path]; | 428 [args replaceObjectAtIndex:0 withObject:ns_new_path]; |
| 397 [ns_plist setObject:args forKey:@ LAUNCH_JOBKEY_PROGRAMARGUMENTS]; | 429 [ns_plist setObject:args forKey:@ LAUNCH_JOBKEY_PROGRAMARGUMENTS]; |
| 398 if (!Launchd::GetInstance()->WritePlistToFile(Launchd::User, | 430 if (!Launchd::GetInstance()->WritePlistToFile(Launchd::User, |
| 399 Launchd::Agent, | 431 Launchd::Agent, |
| 400 name, | 432 name, |
| 401 plist)) { | 433 plist)) { |
| 402 DLOG(ERROR) << "Unable to rewrite plist."; | 434 DLOG(ERROR) << "Unable to rewrite plist."; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 430 CFErrorRef err = NULL; | 462 CFErrorRef err = NULL; |
| 431 if (!Launchd::GetInstance()->RemoveJob(label, &err)) { | 463 if (!Launchd::GetInstance()->RemoveJob(label, &err)) { |
| 432 base::ScopedCFTypeRef<CFErrorRef> scoped_err(err); | 464 base::ScopedCFTypeRef<CFErrorRef> scoped_err(err); |
| 433 DLOG(ERROR) << "RemoveJob " << err; | 465 DLOG(ERROR) << "RemoveJob " << err; |
| 434 // Exiting with zero, so launchd doesn't restart the process. | 466 // Exiting with zero, so launchd doesn't restart the process. |
| 435 exit(0); | 467 exit(0); |
| 436 } | 468 } |
| 437 } | 469 } |
| 438 } | 470 } |
| 439 } | 471 } |
| OLD | NEW |