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

Side by Side Diff: chrome/common/service_process_util_mac.mm

Issue 6660001: Getting service process on Mac to handle having things moved/changed underneath it. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixed up phajdan's comments, got things working properly Created 9 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 #include "chrome/common/service_process_util_posix.h" 5 #include "chrome/common/service_process_util_posix.h"
6 6
7 #import <Foundation/Foundation.h> 7 #import <Foundation/Foundation.h>
8 #include <launch.h> 8 #include <launch.h>
9 #include <syslog.h>
Mark Mentovai 2011/03/11 20:13:52 Why?
9 10
10 #include "base/command_line.h" 11 #include "base/command_line.h"
11 #include "base/file_path.h" 12 #include "base/file_path.h"
13 #include "base/file_util.h"
12 #include "base/mac/foundation_util.h" 14 #include "base/mac/foundation_util.h"
13 #include "base/mac/mac_util.h" 15 #include "base/mac/mac_util.h"
14 #include "base/mac/scoped_nsautorelease_pool.h" 16 #include "base/mac/scoped_nsautorelease_pool.h"
15 #include "base/path_service.h" 17 #include "base/path_service.h"
18 #include "base/process_util.h"
16 #include "base/scoped_nsobject.h" 19 #include "base/scoped_nsobject.h"
20 #include "base/stringprintf.h"
17 #include "base/string_util.h" 21 #include "base/string_util.h"
18 #include "base/sys_string_conversions.h" 22 #include "base/sys_string_conversions.h"
19 #include "base/threading/thread_restrictions.h" 23 #include "base/threading/thread_restrictions.h"
20 #include "base/version.h" 24 #include "base/version.h"
21 #include "chrome/common/child_process_host.h" 25 #include "chrome/common/child_process_host.h"
22 #include "chrome/common/chrome_paths.h" 26 #include "chrome/common/chrome_paths.h"
23 #include "chrome/common/chrome_switches.h" 27 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/chrome_version_info.h" 28 #include "chrome/common/chrome_version_info.h"
25 #include "third_party/GTM/Foundation/GTMServiceManagement.h" 29 #include "third_party/GTM/Foundation/GTMServiceManagement.h"
26 30
27 namespace { 31 namespace {
28 32
33 #define kServiceProcessSessionType "Background"
34
29 NSString* GetServiceProcessLaunchDFileName() { 35 NSString* GetServiceProcessLaunchDFileName() {
30 NSString *bundle_id = [base::mac::MainAppBundle() bundleIdentifier]; 36 NSString *bundle_id = [base::mac::MainAppBundle() bundleIdentifier];
31 NSString *label = [bundle_id stringByAppendingPathExtension:@"plist"]; 37 NSString *label = [bundle_id stringByAppendingPathExtension:@"plist"];
32 return label; 38 return label;
33 } 39 }
34 40
35 NSString* GetServiceProcessLaunchDLabel() { 41 NSString* GetServiceProcessLaunchDLabel() {
36 NSString *bundle_id = [base::mac::MainAppBundle() bundleIdentifier]; 42 NSString *bundle_id = [base::mac::MainAppBundle() bundleIdentifier];
37 NSString *label = [bundle_id stringByAppendingString:@".service_process"]; 43 NSString *label = [bundle_id stringByAppendingString:@".service_process"];
38 FilePath user_data_dir; 44 FilePath user_data_dir;
(...skipping 13 matching lines...) Expand all
52 NSString* GetServiceProcessLaunchDSocketEnvVar() { 58 NSString* GetServiceProcessLaunchDSocketEnvVar() {
53 NSString *label = GetServiceProcessLaunchDLabel(); 59 NSString *label = GetServiceProcessLaunchDLabel();
54 NSString *env_var = [label stringByReplacingOccurrencesOfString:@"." 60 NSString *env_var = [label stringByReplacingOccurrencesOfString:@"."
55 withString:@"_"]; 61 withString:@"_"];
56 env_var = [env_var stringByAppendingString:@"_SOCKET"]; 62 env_var = [env_var stringByAppendingString:@"_SOCKET"];
57 env_var = [env_var uppercaseString]; 63 env_var = [env_var uppercaseString];
58 return env_var; 64 return env_var;
59 } 65 }
60 66
61 // Creates the path that it returns. Must be called on the FILE thread. 67 // Creates the path that it returns. Must be called on the FILE thread.
62 NSURL* GetUserAgentPath() { 68 NSURL* GetUserAgentURL() {
63 NSArray* library_paths = NSSearchPathForDirectoriesInDomains( 69 NSArray* library_paths = NSSearchPathForDirectoriesInDomains(
64 NSLibraryDirectory, NSUserDomainMask, true); 70 NSLibraryDirectory, NSUserDomainMask, true);
65 DCHECK_EQ([library_paths count], 1U); 71 DCHECK_EQ([library_paths count], 1U);
66 NSString* library_path = [library_paths objectAtIndex:0]; 72 NSString* library_path = [library_paths objectAtIndex:0];
67 NSString* launch_agents_path = 73 NSString* launch_agents_path =
68 [library_path stringByAppendingPathComponent:@"LaunchAgents"]; 74 [library_path stringByAppendingPathComponent:@"LaunchAgents"];
69 75
70 NSError* err; 76 NSError* err;
71 if (![[NSFileManager defaultManager] createDirectoryAtPath:launch_agents_path 77 if (![[NSFileManager defaultManager] createDirectoryAtPath:launch_agents_path
72 withIntermediateDirectories:YES 78 withIntermediateDirectories:YES
73 attributes:nil 79 attributes:nil
74 error:&err]) { 80 error:&err]) {
75 LOG(ERROR) << "GetUserAgentPath: " << err; 81 LOG(ERROR) << "GetUserAgentURL: " << err;
76 } 82 }
77 83
78 NSString* plist_file_path = 84 NSString* plist_file_path =
79 [launch_agents_path 85 [launch_agents_path
80 stringByAppendingPathComponent:GetServiceProcessLaunchDFileName()]; 86 stringByAppendingPathComponent:GetServiceProcessLaunchDFileName()];
81 return [NSURL fileURLWithPath:plist_file_path isDirectory:NO]; 87 return [NSURL fileURLWithPath:plist_file_path isDirectory:NO];
82 } 88 }
83 89
84 } 90 class ExecPathComponentWatcherDelegate
91 : public base::FilePathComponentWatcher::Delegate {
92 public:
93 ExecPathComponentWatcherDelegate(CFDictionaryRef launchd_conf,
94 ServiceProcessState *process_state)
95 : launchd_conf_(launchd_conf), process_state_(process_state) { }
96 virtual ~ExecPathComponentWatcherDelegate();
97 virtual void OnFilePathComponentsChanged(
98 base::FilePathComponentWatcher* watcher,
99 const FilePath& old_path,
100 const FilePath& new_path) OVERRIDE;
101
102 private:
103 base::mac::ScopedCFTypeRef<CFDictionaryRef> launchd_conf_;
104 ServiceProcessState* process_state_;
105 };
106
107 } // namespace
85 108
86 // Gets the name of the service process IPC channel. 109 // Gets the name of the service process IPC channel.
87 IPC::ChannelHandle GetServiceProcessChannel() { 110 IPC::ChannelHandle GetServiceProcessChannel() {
88 std::string socket_path; 111 std::string socket_path;
89 scoped_nsobject<NSDictionary> dictionary( 112 scoped_nsobject<NSDictionary> dictionary(
90 base::mac::CFToNSCast(GTMCopyLaunchdExports())); 113 base::mac::CFToNSCast(GTMCopyLaunchdExports()));
91 NSString *ns_socket_path = 114 NSString *ns_socket_path =
92 [dictionary objectForKey:GetServiceProcessLaunchDSocketEnvVar()]; 115 [dictionary objectForKey:GetServiceProcessLaunchDSocketEnvVar()];
93 if (ns_socket_path) { 116 if (ns_socket_path) {
94 socket_path = base::SysNSStringToUTF8(ns_socket_path); 117 socket_path = base::SysNSStringToUTF8(ns_socket_path);
Mark Mentovai 2011/03/11 20:13:52 Uh-oh, seems that the existing code isn’t using -[
95 } 118 }
96 return IPC::ChannelHandle(socket_path); 119 return IPC::ChannelHandle(socket_path);
97 } 120 }
98 121
99 bool ForceServiceProcessShutdown(const std::string& /* version */, 122 bool ForceServiceProcessShutdown(const std::string& /* version */,
100 base::ProcessId /* process_id */) { 123 base::ProcessId /* process_id */) {
101 NSString* label = GetServiceProcessLaunchDLabel(); 124 NSString* label = GetServiceProcessLaunchDLabel();
102 CFErrorRef err = NULL; 125 CFErrorRef err = NULL;
103 bool ret = GTMSMJobRemove(reinterpret_cast<CFStringRef>(label), &err); 126 bool ret = GTMSMJobRemove(reinterpret_cast<CFStringRef>(label), &err);
104 if (!ret) { 127 if (!ret) {
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 if (!InitializeState()) { 182 if (!InitializeState()) {
160 return false; 183 return false;
161 } 184 }
162 CFErrorRef err = NULL; 185 CFErrorRef err = NULL;
163 state_->launchd_conf_.reset(GTMSMJobCheckIn(&err)); 186 state_->launchd_conf_.reset(GTMSMJobCheckIn(&err));
164 if (!state_->launchd_conf_.get()) { 187 if (!state_->launchd_conf_.get()) {
165 LOG(ERROR) << "InitializePlatformState: " << err; 188 LOG(ERROR) << "InitializePlatformState: " << err;
166 CFRelease(err); 189 CFRelease(err);
167 return false; 190 return false;
168 } 191 }
192
193
194 return true;
195 }
196
197 bool ServiceProcessState::SignalReadyPlatformSpecific() {
sanjeevr 2011/03/11 08:26:42 It seems kind of cheating to use the SignalReady t
198 CFStringRef exe_path = reinterpret_cast<CFStringRef>(CFDictionaryGetValue(
199 state_->launchd_conf_, CFSTR(LAUNCH_JOBKEY_PROGRAM)));
200 if (!exe_path) {
201 return false;
202 }
203 FilePath executable_path = FilePath(base::SysCFStringRefToUTF8(exe_path));
Mark Mentovai 2011/03/11 20:13:52 This is wrong. Use CFStringGetFileSystemRepresenta
204 if (!state_->executable_watcher_.Watch(executable_path,
205 new ExecPathComponentWatcherDelegate(state_->launchd_conf_, this))) {
206 return false;
207 }
169 return true; 208 return true;
170 } 209 }
171 210
172 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { 211 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() {
173 CHECK(state_); 212 CHECK(state_);
174 NSDictionary *ns_launchd_conf = base::mac::CFToNSCast(state_->launchd_conf_); 213 NSDictionary *ns_launchd_conf = base::mac::CFToNSCast(state_->launchd_conf_);
175 NSDictionary* socket_dict = 214 NSDictionary* socket_dict =
176 [ns_launchd_conf objectForKey:@ LAUNCH_JOBKEY_SOCKETS]; 215 [ns_launchd_conf objectForKey:@ LAUNCH_JOBKEY_SOCKETS];
177 NSArray* sockets = 216 NSArray* sockets =
178 [socket_dict objectForKey:GetServiceProcessLaunchDSocketKey()]; 217 [socket_dict objectForKey:GetServiceProcessLaunchDSocketKey()];
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 // relaunch the service automatically in any other case than exiting 298 // relaunch the service automatically in any other case than exiting
260 // cleanly with a 0 return code. 299 // cleanly with a 0 return code.
261 NSDictionary *keep_alive = 300 NSDictionary *keep_alive =
262 [NSDictionary 301 [NSDictionary
263 dictionaryWithObject:[NSNumber numberWithBool:NO] 302 dictionaryWithObject:[NSNumber numberWithBool:NO]
264 forKey:@ LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT]; 303 forKey:@ LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT];
265 NSDictionary *auto_launchd_plist = 304 NSDictionary *auto_launchd_plist =
266 [[NSDictionary alloc] initWithObjectsAndKeys: 305 [[NSDictionary alloc] initWithObjectsAndKeys:
267 [NSNumber numberWithBool:YES], @ LAUNCH_JOBKEY_RUNATLOAD, 306 [NSNumber numberWithBool:YES], @ LAUNCH_JOBKEY_RUNATLOAD,
268 keep_alive, @ LAUNCH_JOBKEY_KEEPALIVE, 307 keep_alive, @ LAUNCH_JOBKEY_KEEPALIVE,
269 @"Background", @ LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE, 308 @ kServiceProcessSessionType, @ LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE,
270 nil]; 309 nil];
271 [launchd_plist addEntriesFromDictionary:auto_launchd_plist]; 310 [launchd_plist addEntriesFromDictionary:auto_launchd_plist];
272 } 311 }
273 return reinterpret_cast<CFDictionaryRef>(launchd_plist); 312 return reinterpret_cast<CFDictionaryRef>(launchd_plist);
274 } 313 }
275 314
276 // Writes the launchd property list into the user's LaunchAgents directory, 315 // Writes the launchd property list into the user's LaunchAgents directory,
277 // creating that directory if needed. This will cause the service process to be 316 // creating that directory if needed. This will cause the service process to be
278 // auto launched on the next user login. 317 // auto launched on the next user login.
279 bool ServiceProcessState::AddToAutoRun() { 318 bool ServiceProcessState::AddToAutoRun() {
280 // We're creating directories and writing a file. 319 // We're creating directories and writing a file.
281 base::ThreadRestrictions::AssertIOAllowed(); 320 base::ThreadRestrictions::AssertIOAllowed();
282 DCHECK(autorun_command_line_.get()); 321 DCHECK(autorun_command_line_.get());
283 322
284 base::mac::ScopedNSAutoreleasePool pool; 323 base::mac::ScopedNSAutoreleasePool pool;
285 scoped_nsobject<NSDictionary> plist( 324 scoped_nsobject<NSDictionary> plist(
286 base::mac::CFToNSCast(CreateServiceProcessLaunchdPlist( 325 base::mac::CFToNSCast(CreateServiceProcessLaunchdPlist(
287 autorun_command_line_.get(), true))); 326 autorun_command_line_.get(), true)));
288 NSURL* plist_url = GetUserAgentPath(); 327 NSURL* plist_url = GetUserAgentURL();
289 return [plist writeToURL:plist_url atomically:YES]; 328 return [plist writeToURL:plist_url atomically:YES];
290 } 329 }
291 330
292 bool ServiceProcessState::RemoveFromAutoRun() { 331 bool ServiceProcessState::RemoveFromAutoRun() {
293 // We're killing a file. 332 // We're killing a file.
294 base::ThreadRestrictions::AssertIOAllowed(); 333 base::ThreadRestrictions::AssertIOAllowed();
295 334
296 base::mac::ScopedNSAutoreleasePool pool; 335 base::mac::ScopedNSAutoreleasePool pool;
297 NSURL* plist_url = GetUserAgentPath(); 336 NSURL* plist_url = GetUserAgentURL();
298 SInt32 error = 0; 337 SInt32 error = 0;
299 if (!CFURLDestroyResource(reinterpret_cast<CFURLRef>(plist_url), &error)) { 338 if (!CFURLDestroyResource(reinterpret_cast<CFURLRef>(plist_url), &error)) {
300 LOG(ERROR) << "RemoveFromAutoRun: " << error; 339 LOG(ERROR) << "RemoveFromAutoRun: " << error;
301 return false; 340 return false;
302 } 341 }
303 return true; 342 return true;
304 } 343 }
344
345 ExecPathComponentWatcherDelegate::~ExecPathComponentWatcherDelegate() {
346 }
347
348 void ExecPathComponentWatcherDelegate::OnFilePathComponentsChanged(
349 base::FilePathComponentWatcher* watcher, const FilePath& old_path,
350 const FilePath& new_path) {
351 base::mac::ScopedNSAutoreleasePool pool;
352 bool needs_shutdown = false;
353 bool needs_restart = false;
354 bool needs_plist_rewrite = false;
355 bool needs_plist_deletion = false;
356 if (access(new_path.value().c_str(), W_OK) != 0) {
357 // Can we execute it?
Mark Mentovai 2011/03/11 20:13:52 Remember, no “we.” This is better: // Is it ex
358 needs_shutdown = true;
359 needs_plist_deletion = true;
360 } else if (file_util::IsInTrash(new_path)) {
361 // Is it in the trash?
362 needs_shutdown = true;
363 needs_plist_deletion = true;
364 } else if (!file_util::AreReferringToSameObject(old_path, new_path)) {
365 // Did it get moved?
366 needs_restart = true;
367 needs_plist_rewrite = true;
Mark Mentovai 2011/03/11 20:13:52 Judging from this logic, it seems that needs sh
368 } else {
369 // Nothing exciting happened. Let's continue to ask for events.
Mark Mentovai 2011/03/11 20:13:52 s/Let's c/C/
370 if (!watcher->Watch(new_path, this)) {
Mark Mentovai 2011/03/11 20:13:52 OK, so it *is* possible to call Watch on an existi
371 LOG(ERROR) << "Watch failed: " << new_path.value();
372 }
373 }
374 if (needs_plist_rewrite) {
375 NSURL* plist_url = GetUserAgentURL();
376 NSMutableDictionary* plist
377 = [NSMutableDictionary dictionaryWithContentsOfURL:plist_url];
Mark Mentovai 2011/03/11 20:13:52 Put the = on the preceding line, and indent this o
378 if (plist) {
379 NSString* ns_new_path = base::SysUTF8ToNSString(new_path.value());
Mark Mentovai 2011/03/11 20:13:52 Why not just +[NSString stringWithUTF8String:]?
380 [plist setObject:ns_new_path forKey:@ LAUNCH_JOBKEY_PROGRAM];
381 scoped_nsobject<NSMutableArray> args(
382 [[plist objectForKey:@ LAUNCH_JOBKEY_PROGRAMARGUMENTS] mutableCopy]);
383 [args replaceObjectAtIndex:0 withObject:ns_new_path];
384 [plist setObject:args forKey:@ LAUNCH_JOBKEY_PROGRAMARGUMENTS];
385 if (![plist writeToURL:plist_url atomically:YES]) {
386 LOG(ERROR) << "Unable to rewrite plist.";
387 }
388 } else {
389 LOG(ERROR) << "Unable to read plist.";
390 }
391 } else if (needs_plist_deletion) {
392 if (!process_state_->RemoveFromAutoRun()) {
393 LOG(ERROR) << "Unable to RemoveFromAutoRun.";
394 }
395 }
396 if (needs_shutdown || needs_restart) {
397 NSURL *plist_url = GetUserAgentURL();
398 std::string plist_path(base::SysNSStringToUTF8([plist_url path]));
Mark Mentovai 2011/03/11 20:13:52 No, use [plist_url fileSystemRepresentation].
399 std::vector<std::string> argv;
400
401 if (needs_shutdown) {
402 VLOG(1) << "Shutting down using launchctl";
403 argv.push_back("/bin/launchctl");
404 argv.push_back("unload");
405 argv.push_back("-S");
406 argv.push_back(kServiceProcessSessionType);
407 argv.push_back(plist_path);
408 } else if (needs_restart) {
Mark Mentovai 2011/03/11 20:13:52 This can just be |else|, look at the enclosing con
409 VLOG(1) << "Restarting using launchctl";
Mark Mentovai 2011/03/11 20:13:52 I assume that as soon as you run launchctl unload,
410 argv.push_back("/bin/sh");
Mark Mentovai 2011/03/11 20:13:52 /bin/bash if you’re using --noprofile.
411 argv.push_back("--noprofile");
412 argv.push_back("-c");
413 std::string command = base::StringPrintf(
414 "/bin/launchctl unload -S %s '%s';/bin/launchctl load -S %s '%s';",
Mark Mentovai 2011/03/11 20:13:52 The only way I’ll let you pass arbitrary arguments
415 kServiceProcessSessionType, plist_path.c_str(),
416 kServiceProcessSessionType, plist_path.c_str());
417 argv.push_back(command);
418 }
419 base::ProcessHandle handle;
420 if (!base::LaunchAppInNewProcessGroup(argv,
421 base::environment_vector(),
422 base::file_handle_mapping_vector(),
423 NO,
424 &handle)) {
425 LOG(ERROR) << "LaunchAppInNewProcessGroup";
426 }
Mark Mentovai 2011/03/11 20:13:52 What are you expecting to happen here? Be killed?
427 }
428 }
429
Mark Mentovai 2011/03/11 20:13:52 Blank line at EOF.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698